From: Aric Stewart Subject: [PATCH v4 15/20] winehid.sys: implement IRP_MJ_INTERNAL_DEVICE_CONTROL for linux hidraw devices Message-Id: <5be169e5-e730-7677-4db4-cca48ceb17e0@codeweavers.com> Date: Tue, 6 Sep 2016 10:40:21 -0500 v4: respect irp->IoStatus.u.Status in the dispatch handler Signed-off-by: Aric Stewart --- dlls/winehid.sys/minidriver_hidraw.c | 216 +++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/dlls/winehid.sys/minidriver_hidraw.c b/dlls/winehid.sys/minidriver_hidraw.c index 1460c5d..b49292d 100644 --- a/dlls/winehid.sys/minidriver_hidraw.c +++ b/dlls/winehid.sys/minidriver_hidraw.c @@ -78,6 +78,221 @@ static HANDLE device_loop_handle; static HID_MINIDRIVER_REGISTRATION registration; static int control_pipe[2]; +static NTSTATUS WINAPI dispatch_ioctl(DEVICE_OBJECT *device, IRP *irp) +{ + NTSTATUS rc = irp->IoStatus.u.Status; + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + HR_DEVICE_EXTENSION *extension = (HR_DEVICE_EXTENSION*)(((HID_DEVICE_EXTENSION*)device->DeviceExtension)->MiniDeviceExtension); + + irp->IoStatus.Information = 0; + + switch (irpsp->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_GET_PHYSICAL_DESCRIPTOR: + TRACE("IOCTL_GET_PHYSICAL_DESCRIPTOR\n"); + break; + case IOCTL_HID_ACTIVATE_DEVICE: + TRACE("IOCTL_HID_ACTIVATE_DEVICE\n"); + break; + case IOCTL_HID_DEACTIVATE_DEVICE: + TRACE("IOCTL_HID_DEACTIVATE_DEVICE\n"); + break; + case IOCTL_HID_GET_DEVICE_ATTRIBUTES: + { + char *endptr; + struct udev_device *usbdev; + PHID_DEVICE_ATTRIBUTES attr = (PHID_DEVICE_ATTRIBUTES)irp->UserBuffer; + + TRACE("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n"); + + RtlZeroMemory(attr, sizeof(HID_DEVICE_ATTRIBUTES)); + attr->Size = sizeof(HID_DEVICE_ATTRIBUTES); + + usbdev = udev_device_get_parent_with_subsystem_devtype( + extension->udev, "usb", "usb_device"); + if (usbdev) + { + attr->VendorID = strtol(udev_device_get_sysattr_value(usbdev,"idVendor"), &endptr, 16); + attr->ProductID = strtol(udev_device_get_sysattr_value(usbdev,"idProduct"), &endptr, 16); + attr->VersionNumber = 0; + } + else + { + struct hidraw_devinfo devinfo; + if (ioctl(extension->joyfd, HIDIOCGRAWINFO, &devinfo) == -1) + WARN("ioctl(HIDIOCGRAWINFO) failed: %d %s\n", errno, strerror(errno)); + else + { + attr->VendorID = devinfo.vendor; + attr->ProductID = devinfo.product; + attr->VersionNumber = 0; + } + } + + irp->IoStatus.u.Status = rc = STATUS_SUCCESS; + irp->IoStatus.Information = sizeof(HID_DEVICE_ATTRIBUTES); + break; + } + case IOCTL_HID_GET_DEVICE_DESCRIPTOR: + { + int length = 0; + HID_DESCRIPTOR *descriptor = (HID_DESCRIPTOR*)irp->UserBuffer; + + TRACE("IOCTL_HID_GET_DEVICE_DESCRIPTOR\n"); + + if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_DESCRIPTOR)) + { + irp->IoStatus.u.Status = rc = STATUS_BUFFER_TOO_SMALL; + break; + } + + if (ioctl(extension->joyfd, HIDIOCGRDESCSIZE, &length) == -1) + { + WARN("ioctl(HIDIOCGRDESCSIZE) failed: %d %s\n", errno, strerror(errno)); + irp->IoStatus.u.Status = rc = STATUS_UNSUCCESSFUL; + break; + } + ZeroMemory(descriptor, sizeof(*descriptor)); + descriptor->bLength = sizeof(*descriptor); + descriptor->bDescriptorType = HID_HID_DESCRIPTOR_TYPE; + descriptor->bcdHID = HID_REVISION; + descriptor->bCountry = 0; + descriptor->bNumDescriptors = 1; + descriptor->DescriptorList[0].bReportType = HID_REPORT_DESCRIPTOR_TYPE; + descriptor->DescriptorList[0].wReportLength = length; + + irp->IoStatus.Information = sizeof(*descriptor); + irp->IoStatus.u.Status = rc = STATUS_SUCCESS; + break; + } + case IOCTL_HID_GET_FEATURE: + TRACE("IOCTL_HID_GET_FEATURE\n"); + break; + case IOCTL_HID_GET_INDEXED_STRING: + TRACE("IOCTL_HID_GET_INDEXED_STRING\n"); + break; + case IOCTL_HID_GET_INPUT_REPORT: + TRACE_(hid_report)("IOCTL_HID_GET_INPUT_REPORT\n"); + ZeroMemory(((PHID_XFER_PACKET)(irp->UserBuffer))->reportBuffer, + ((PHID_XFER_PACKET)(irp->UserBuffer))->reportBufferLen); + memcpy(irp->UserBuffer, extension->last_report, extension->report_size); + irp->IoStatus.u.Status = rc = STATUS_SUCCESS; + irp->IoStatus.Information = sizeof(HID_XFER_PACKET); + break; + case IOCTL_HID_GET_REPORT_DESCRIPTOR: + { + struct hidraw_report_descriptor descriptor; + TRACE("IOCTL_HID_GET_REPORT_DESCRIPTOR\n"); + + if (ioctl(extension->joyfd, HIDIOCGRDESCSIZE, &descriptor.size) == -1) + { + WARN("ioctl(HIDIOCGRDESCSIZE) failed: %d %s\n", errno, strerror(errno)); + irp->IoStatus.u.Status = rc = STATUS_UNSUCCESSFUL; + break; + } + if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < descriptor.size) + { + irp->IoStatus.u.Status = rc = STATUS_BUFFER_TOO_SMALL; + break; + } + if (ioctl(extension->joyfd, HIDIOCGRDESC , &descriptor) == -1) + { + WARN("ioctl(HIDIOCGRDESC) failed: %d %s\n", errno, strerror(errno)); + irp->IoStatus.u.Status = rc = STATUS_UNSUCCESSFUL; + break; + } + memcpy(irp->UserBuffer, descriptor.value, descriptor.size); + irp->IoStatus.Information = descriptor.size; + irp->IoStatus.u.Status = rc = STATUS_SUCCESS; + break; + } + case IOCTL_HID_GET_STRING: + { + DWORD index = (DWORD)irpsp->Parameters.DeviceIoControl.Type3InputBuffer; + struct udev_device *usbdev; + const char *str = NULL; + + TRACE("IOCTL_HID_GET_STRING\n"); + + usbdev = udev_device_get_parent_with_subsystem_devtype( + extension->udev, "usb", "usb_device"); + if (usbdev) + { + switch (index) + { + case HID_STRING_ID_IPRODUCT: + str = udev_device_get_sysattr_value(usbdev, "product"); + break; + case HID_STRING_ID_IMANUFACTURER: + str = udev_device_get_sysattr_value(usbdev, "manufacturer"); + break; + case HID_STRING_ID_ISERIALNUMBER: + str = udev_device_get_sysattr_value(usbdev, "serial"); + break; + default: + ERR("Unhandled string index %i\n", index); + } + } + else + { + static char str_buffer[MAX_PATH] = {0}; + str = str_buffer; + switch (index) + { + case HID_STRING_ID_IPRODUCT: + ioctl(extension->joyfd, HIDIOCGRAWNAME(MAX_PATH), str); + break; + case HID_STRING_ID_IMANUFACTURER: + break; + case HID_STRING_ID_ISERIALNUMBER: + break; + default: + ERR("Unknown string index\n"); + } + } + + if (str && str[0]) + { + WCHAR *buffer = (WCHAR*)(irp->UserBuffer); + int length = irpsp->Parameters.DeviceIoControl.OutputBufferLength/sizeof(WCHAR); + MultiByteToWideChar(CP_ACP, 0, str, -1, buffer, length); + irp->IoStatus.Information = (lstrlenW(buffer)+1) * sizeof(WCHAR); + } + + irp->IoStatus.u.Status = rc = STATUS_SUCCESS; + break; + } + case IOCTL_HID_READ_REPORT: + { + TRACE_(hid_report)("IOCTL_HID_READ_REPORT\n"); + InsertTailList(&extension->irp_queue, &irp->Tail.Overlay.ListEntry); + rc = STATUS_PENDING; + break; + } + case IOCTL_HID_SET_FEATURE: + TRACE("IOCTL_HID_SET_FEATURE\n"); + break; + case IOCTL_HID_SET_OUTPUT_REPORT: + TRACE("IOCTL_HID_SET_OUTPUT_REPORT\n"); + break; + case IOCTL_HID_WRITE_REPORT: + TRACE("IOCTL_HID_WRITE_REPORT\n"); + break; + default: + { + ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode; + FIXME("Unsupported ioctl %i %x (device=%x access=%x func=%x method=%x)\n", + extension->joyfd, code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3); + break; + } + } + + if (rc != STATUS_PENDING) + IoCompleteRequest( irp, IO_NO_INCREMENT ); + + return rc; +} + VOID WINAPI unload(DRIVER_OBJECT *driver) { TRACE("Linux Hidraw Driver Unload\n"); @@ -249,6 +464,7 @@ NTSTATUS WINAPI hidraw_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *path) device_loop_handle = CreateThread(NULL, 0, deviceloop_thread, &event, 0, NULL); hr_driver_obj = driver; + driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = dispatch_ioctl; driver->DriverUnload = unload; driver->DriverExtension->AddDevice = add_device;