winebus.sys: Queue HID reports instead of overwritting the last one.
If the hidclass.sys read requests aren't done fast enough. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51824 Signed-off-by: Rémi Bernon <rbernon@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
068511f8cb
commit
c9003fab66
|
@ -54,6 +54,13 @@ static DEVICE_OBJECT *bus_fdo;
|
||||||
|
|
||||||
static HANDLE driver_key;
|
static HANDLE driver_key;
|
||||||
|
|
||||||
|
struct hid_report
|
||||||
|
{
|
||||||
|
struct list entry;
|
||||||
|
ULONG length;
|
||||||
|
BYTE buffer[1];
|
||||||
|
};
|
||||||
|
|
||||||
enum device_state
|
enum device_state
|
||||||
{
|
{
|
||||||
DEVICE_STATE_STOPPED,
|
DEVICE_STATE_STOPPED,
|
||||||
|
@ -77,10 +84,7 @@ struct device_extension
|
||||||
HIDP_DEVICE_DESC collection_desc;
|
HIDP_DEVICE_DESC collection_desc;
|
||||||
|
|
||||||
BYTE *last_reports[256];
|
BYTE *last_reports[256];
|
||||||
BYTE *last_report;
|
struct list reports;
|
||||||
DWORD last_report_size;
|
|
||||||
BOOL last_report_read;
|
|
||||||
DWORD buffer_size;
|
|
||||||
IRP *pending_read;
|
IRP *pending_read;
|
||||||
|
|
||||||
struct unix_device *unix_device;
|
struct unix_device *unix_device;
|
||||||
|
@ -307,11 +311,8 @@ static DEVICE_OBJECT *bus_create_hid_device(struct device_desc *desc, struct uni
|
||||||
ext->device = device;
|
ext->device = device;
|
||||||
ext->desc = *desc;
|
ext->desc = *desc;
|
||||||
ext->index = get_device_index(desc);
|
ext->index = get_device_index(desc);
|
||||||
ext->last_report = NULL;
|
|
||||||
ext->last_report_size = 0;
|
|
||||||
ext->last_report_read = TRUE;
|
|
||||||
ext->buffer_size = 0;
|
|
||||||
ext->unix_device = unix_device;
|
ext->unix_device = unix_device;
|
||||||
|
list_init(&ext->reports);
|
||||||
|
|
||||||
InitializeCriticalSection(&ext->cs);
|
InitializeCriticalSection(&ext->cs);
|
||||||
ext->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
|
ext->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
|
||||||
|
@ -397,63 +398,43 @@ static DWORD check_bus_option(const WCHAR *option, DWORD default_value)
|
||||||
return default_value;
|
return default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NTSTATUS deliver_last_report(struct device_extension *ext, DWORD buffer_length, BYTE* buffer, ULONG_PTR *out_length)
|
static BOOL deliver_next_report(struct device_extension *ext, IRP *irp)
|
||||||
{
|
{
|
||||||
if (buffer_length < ext->last_report_size)
|
struct hid_report *report;
|
||||||
{
|
struct list *entry;
|
||||||
*out_length = 0;
|
|
||||||
return STATUS_BUFFER_TOO_SMALL;
|
if (!(entry = list_head(&ext->reports))) return FALSE;
|
||||||
}
|
report = LIST_ENTRY(entry, struct hid_report, entry);
|
||||||
else
|
list_remove(&report->entry);
|
||||||
{
|
|
||||||
if (ext->last_report)
|
memcpy(irp->UserBuffer, report->buffer, report->length);
|
||||||
memcpy(buffer, ext->last_report, ext->last_report_size);
|
irp->IoStatus.Information = report->length;
|
||||||
*out_length = ext->last_report_size;
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
RtlFreeHeap(GetProcessHeap(), 0, report);
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_hid_report(DEVICE_OBJECT *device, BYTE *report, DWORD length)
|
static void process_hid_report(DEVICE_OBJECT *device, BYTE *report_buf, DWORD report_len)
|
||||||
{
|
{
|
||||||
struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
|
struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
|
||||||
IO_STACK_LOCATION *stack;
|
ULONG size = offsetof(struct hid_report, buffer[report_len]);
|
||||||
ULONG buffer_len;
|
struct hid_report *report;
|
||||||
IRP *irp;
|
IRP *irp;
|
||||||
|
|
||||||
if (!length || !report)
|
if (!(report = RtlAllocateHeap(GetProcessHeap(), 0, size))) return;
|
||||||
return;
|
memcpy(report->buffer, report_buf, report_len);
|
||||||
|
report->length = report_len;
|
||||||
|
|
||||||
RtlEnterCriticalSection(&ext->cs);
|
RtlEnterCriticalSection(&ext->cs);
|
||||||
if (length > ext->buffer_size)
|
list_add_tail(&ext->reports, &report->entry);
|
||||||
{
|
|
||||||
RtlFreeHeap(GetProcessHeap(), 0, ext->last_report);
|
|
||||||
ext->last_report = RtlAllocateHeap(GetProcessHeap(), 0, length);
|
|
||||||
if (!ext->last_report)
|
|
||||||
{
|
|
||||||
ERR_(hid_report)("Failed to alloc last report\n");
|
|
||||||
ext->buffer_size = 0;
|
|
||||||
ext->last_report_size = 0;
|
|
||||||
ext->last_report_read = TRUE;
|
|
||||||
RtlLeaveCriticalSection(&ext->cs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ext->buffer_size = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(ext->last_report, report, length);
|
if (!ext->collection_desc.ReportIDs[0].ReportID) memcpy(ext->last_reports[0], report_buf, report_len);
|
||||||
ext->last_report_size = length;
|
else memcpy(ext->last_reports[report_buf[0]], report_buf, report_len);
|
||||||
ext->last_report_read = FALSE;
|
|
||||||
|
|
||||||
if (!ext->collection_desc.ReportIDs[0].ReportID) memcpy(ext->last_reports[0], report, length);
|
|
||||||
else memcpy(ext->last_reports[report[0]], report, length);
|
|
||||||
|
|
||||||
if ((irp = pop_pending_read(ext)))
|
if ((irp = pop_pending_read(ext)))
|
||||||
{
|
{
|
||||||
stack = IoGetCurrentIrpStackLocation(irp);
|
deliver_next_report(ext, irp);
|
||||||
buffer_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
||||||
irp->IoStatus.Status = deliver_last_report(ext, buffer_len, irp->UserBuffer, &irp->IoStatus.Information);
|
|
||||||
ext->last_report_read = TRUE;
|
|
||||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||||
}
|
}
|
||||||
RtlLeaveCriticalSection(&ext->cs);
|
RtlLeaveCriticalSection(&ext->cs);
|
||||||
|
@ -819,6 +800,7 @@ static NTSTATUS pdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp)
|
||||||
struct device_extension *ext = device->DeviceExtension;
|
struct device_extension *ext = device->DeviceExtension;
|
||||||
NTSTATUS status = irp->IoStatus.Status;
|
NTSTATUS status = irp->IoStatus.Status;
|
||||||
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
|
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
|
||||||
|
struct hid_report *report, *next;
|
||||||
HIDP_REPORT_IDS *reports;
|
HIDP_REPORT_IDS *reports;
|
||||||
ULONG i, size;
|
ULONG i, size;
|
||||||
|
|
||||||
|
@ -885,11 +867,12 @@ static NTSTATUS pdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp)
|
||||||
ext->cs.DebugInfo->Spare[0] = 0;
|
ext->cs.DebugInfo->Spare[0] = 0;
|
||||||
DeleteCriticalSection(&ext->cs);
|
DeleteCriticalSection(&ext->cs);
|
||||||
|
|
||||||
RtlFreeHeap(GetProcessHeap(), 0, ext->last_report);
|
|
||||||
|
|
||||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||||
|
|
||||||
|
LIST_FOR_EACH_ENTRY_SAFE(report, next, &ext->reports, struct hid_report, entry)
|
||||||
|
RtlFreeHeap(GetProcessHeap(), 0, report);
|
||||||
|
|
||||||
reports = ext->collection_desc.ReportIDs;
|
reports = ext->collection_desc.ReportIDs;
|
||||||
for (i = 0; i < ext->collection_desc.ReportIDsLength; ++i)
|
for (i = 0; i < ext->collection_desc.ReportIDsLength; ++i)
|
||||||
{
|
{
|
||||||
|
@ -1046,14 +1029,7 @@ static NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp)
|
||||||
}
|
}
|
||||||
case IOCTL_HID_READ_REPORT:
|
case IOCTL_HID_READ_REPORT:
|
||||||
{
|
{
|
||||||
TRACE_(hid_report)("IOCTL_HID_READ_REPORT\n");
|
if (!deliver_next_report(ext, irp))
|
||||||
if (!ext->last_report_read)
|
|
||||||
{
|
|
||||||
irp->IoStatus.Status = deliver_last_report(ext,
|
|
||||||
buffer_len, irp->UserBuffer, &irp->IoStatus.Information);
|
|
||||||
ext->last_report_read = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
/* hidclass.sys should guarantee this */
|
/* hidclass.sys should guarantee this */
|
||||||
assert(!ext->pending_read);
|
assert(!ext->pending_read);
|
||||||
|
|
Loading…
Reference in New Issue