2024-01-14 14:02:37 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2024 Rémi Bernon for CodeWeavers
|
|
|
|
*
|
|
|
|
* 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 <stdarg.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "ntstatus.h"
|
|
|
|
#define WIN32_NO_STATUS
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "winternl.h"
|
|
|
|
#include "winioctl.h"
|
|
|
|
#include "ntuser.h"
|
|
|
|
|
|
|
|
#include "ddk/wdm.h"
|
|
|
|
#include "ddk/hidport.h"
|
|
|
|
#include "ddk/hidpddi.h"
|
|
|
|
#include "ddk/hidtypes.h"
|
|
|
|
|
|
|
|
#include "wine/hid.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
#include "wine/list.h"
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(hid);
|
|
|
|
|
2024-02-13 10:31:37 +00:00
|
|
|
struct contact
|
|
|
|
{
|
|
|
|
struct list entry;
|
|
|
|
ULONG id;
|
|
|
|
POINT pos;
|
|
|
|
};
|
|
|
|
|
2024-01-14 14:02:37 +00:00
|
|
|
struct device
|
|
|
|
{
|
|
|
|
LONG removed;
|
|
|
|
DEVICE_OBJECT *bus_device;
|
2024-02-13 11:33:18 +00:00
|
|
|
PHIDP_PREPARSED_DATA preparsed;
|
|
|
|
|
2024-02-13 10:38:46 +00:00
|
|
|
FILE_OBJECT dummy_file;
|
|
|
|
IO_STATUS_BLOCK io;
|
|
|
|
ULONG report_len;
|
|
|
|
char *report_buf;
|
|
|
|
|
2024-02-13 10:31:37 +00:00
|
|
|
ULONG contact_count;
|
|
|
|
struct list contacts;
|
|
|
|
|
2024-02-13 11:33:18 +00:00
|
|
|
ULONG caps_count;
|
|
|
|
ULONG contact_max;
|
|
|
|
HIDP_VALUE_CAPS *id_caps;
|
|
|
|
HIDP_VALUE_CAPS *x_caps;
|
|
|
|
HIDP_VALUE_CAPS *y_caps;
|
2024-01-14 14:02:37 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline struct device *impl_from_DEVICE_OBJECT( DEVICE_OBJECT *device )
|
|
|
|
{
|
|
|
|
return (struct device *)device->DeviceExtension;
|
|
|
|
}
|
|
|
|
|
2024-02-13 11:33:18 +00:00
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
2024-02-13 10:38:46 +00:00
|
|
|
static NTSTATUS WINAPI read_completion( DEVICE_OBJECT *device, IRP *irp, void *context );
|
|
|
|
|
|
|
|
static NTSTATUS start_device_read( DEVICE_OBJECT *device )
|
|
|
|
{
|
|
|
|
struct device *impl = impl_from_DEVICE_OBJECT( device );
|
|
|
|
IO_STACK_LOCATION *stack;
|
|
|
|
NTSTATUS status;
|
|
|
|
IRP *irp;
|
|
|
|
|
|
|
|
TRACE( "device %p\n", device );
|
|
|
|
|
|
|
|
irp = IoBuildAsynchronousFsdRequest( IRP_MJ_READ, device, impl->report_buf,
|
|
|
|
impl->report_len, NULL, &impl->io );
|
|
|
|
if (!irp) return STATUS_NO_MEMORY;
|
|
|
|
irp->Tail.Overlay.OriginalFileObject = &impl->dummy_file;
|
|
|
|
stack = IoGetNextIrpStackLocation( irp );
|
|
|
|
stack->FileObject = &impl->dummy_file;
|
|
|
|
|
|
|
|
TRACE( "created irp %p\n", irp );
|
|
|
|
|
|
|
|
IoSetCompletionRoutine( irp, read_completion, device, TRUE, TRUE, TRUE );
|
|
|
|
if ((status = IoCallDriver( impl->bus_device, irp )) && status != STATUS_PENDING) return status;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2024-02-13 10:31:37 +00:00
|
|
|
static void add_contact( struct device *impl, struct list *old_contacts, ULONG id, LONG x, LONG y )
|
|
|
|
{
|
2024-02-13 10:35:33 +00:00
|
|
|
UINT flags = POINTER_MESSAGE_FLAG_INRANGE | POINTER_MESSAGE_FLAG_INCONTACT | POINTER_MESSAGE_FLAG_CONFIDENCE;
|
|
|
|
INPUT input = {.type = INPUT_HARDWARE};
|
2024-02-13 10:31:37 +00:00
|
|
|
struct contact *contact;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY( contact, old_contacts, struct contact, entry )
|
|
|
|
if (contact->id == id) break;
|
|
|
|
|
|
|
|
if (&contact->entry != old_contacts)
|
|
|
|
{
|
2024-02-13 10:35:33 +00:00
|
|
|
input.hi.uMsg = WM_POINTERUPDATE;
|
2024-02-13 10:31:37 +00:00
|
|
|
list_remove( &contact->entry );
|
|
|
|
|
|
|
|
contact->pos.x = x;
|
|
|
|
contact->pos.y = y;
|
|
|
|
TRACE( "updating contact %#lx, pos %s\n", contact->id, wine_dbgstr_point( &contact->pos ) );
|
|
|
|
}
|
|
|
|
else if ((contact = calloc( 1, sizeof(*contact) )))
|
|
|
|
{
|
2024-02-13 10:35:33 +00:00
|
|
|
input.hi.uMsg = WM_POINTERDOWN;
|
|
|
|
flags |= POINTER_MESSAGE_FLAG_NEW;
|
|
|
|
|
2024-02-13 10:31:37 +00:00
|
|
|
contact->id = id;
|
|
|
|
contact->pos.x = x;
|
|
|
|
contact->pos.y = y;
|
|
|
|
TRACE( "new contact %#lx, pos %s\n", contact->id, wine_dbgstr_point( &contact->pos ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERR( "failed to allocate new contact\n" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-02-13 10:35:33 +00:00
|
|
|
input.hi.wParamL = contact->id;
|
|
|
|
input.hi.wParamH = flags;
|
|
|
|
NtUserSendHardwareInput( 0, 0, &input, MAKELPARAM(contact->pos.x, contact->pos.y) );
|
|
|
|
|
2024-02-13 10:31:37 +00:00
|
|
|
list_add_tail( &impl->contacts, &contact->entry );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void release_contacts( struct list *contacts )
|
|
|
|
{
|
|
|
|
struct contact *contact, *next;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE( contact, next, contacts, struct contact, entry )
|
|
|
|
{
|
2024-02-13 10:35:33 +00:00
|
|
|
INPUT input = {.type = INPUT_HARDWARE};
|
|
|
|
ULONG flags = POINTER_MESSAGE_FLAG_CONFIDENCE;
|
|
|
|
|
2024-02-13 10:31:37 +00:00
|
|
|
TRACE( "releasing contact %#lx, pos %s\n", contact->id, wine_dbgstr_point( &contact->pos ) );
|
2024-02-13 10:35:33 +00:00
|
|
|
|
|
|
|
input.hi.uMsg = WM_POINTERUP;
|
|
|
|
input.hi.wParamL = contact->id;
|
|
|
|
input.hi.wParamH = flags;
|
|
|
|
NtUserSendHardwareInput( 0, 0, &input, MAKELPARAM(contact->pos.x, contact->pos.y) );
|
|
|
|
|
2024-02-13 10:31:37 +00:00
|
|
|
list_remove( &contact->entry );
|
|
|
|
free( contact );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-13 10:38:46 +00:00
|
|
|
static void process_hid_report( struct device *impl, char *report_buf, UINT report_len )
|
|
|
|
{
|
2024-02-13 10:31:37 +00:00
|
|
|
struct list old_contacts = LIST_INIT( old_contacts );
|
|
|
|
ULONG i, value, contact_count, usage_count, id;
|
|
|
|
LONG x = 0, y = 0;
|
2024-02-13 10:38:46 +00:00
|
|
|
NTSTATUS status;
|
2024-02-13 10:31:37 +00:00
|
|
|
USHORT usage;
|
2024-02-13 10:38:46 +00:00
|
|
|
|
|
|
|
TRACE( "impl %p, report_buf %p, report_len %u\n", impl, report_buf, report_len );
|
|
|
|
|
|
|
|
status = HidP_GetUsageValue( HidP_Input, HID_USAGE_PAGE_DIGITIZER, 0, HID_USAGE_DIGITIZER_CONTACT_COUNT,
|
|
|
|
&contact_count, impl->preparsed, report_buf, report_len );
|
|
|
|
if (status != HIDP_STATUS_SUCCESS) return;
|
|
|
|
if (contact_count > impl->contact_max)
|
|
|
|
{
|
|
|
|
WARN( "got %lu contacts, capping to %lu.\n", contact_count, impl->contact_max );
|
|
|
|
contact_count = impl->contact_max;
|
|
|
|
}
|
|
|
|
|
2024-02-13 10:31:37 +00:00
|
|
|
list_move_tail( &old_contacts, &impl->contacts );
|
|
|
|
|
|
|
|
for (i = 0; i < impl->caps_count; i++)
|
|
|
|
{
|
|
|
|
USHORT collection = impl->id_caps[i].LinkCollection;
|
|
|
|
|
|
|
|
usage_count = 1;
|
|
|
|
usage = HID_USAGE_DIGITIZER_TIP_SWITCH;
|
|
|
|
status = HidP_GetUsages( HidP_Input, HID_USAGE_PAGE_DIGITIZER, collection, &usage, &usage_count,
|
|
|
|
impl->preparsed, report_buf, report_len );
|
|
|
|
if (status != HIDP_STATUS_SUCCESS || !usage_count) continue;
|
|
|
|
|
|
|
|
status = HidP_GetUsageValue( HidP_Input, HID_USAGE_PAGE_DIGITIZER, collection, HID_USAGE_DIGITIZER_CONTACT_ID,
|
|
|
|
&id, impl->preparsed, report_buf, report_len );
|
|
|
|
if (status != HIDP_STATUS_SUCCESS) continue;
|
|
|
|
|
|
|
|
status = HidP_GetUsageValue( HidP_Input, HID_USAGE_PAGE_GENERIC, collection, HID_USAGE_GENERIC_X,
|
|
|
|
&value, impl->preparsed, report_buf, report_len );
|
|
|
|
if (status != HIDP_STATUS_SUCCESS) continue;
|
|
|
|
else x = scale_value( value, impl->x_caps + i, 0, 65535 );
|
|
|
|
|
|
|
|
status = HidP_GetUsageValue( HidP_Input, HID_USAGE_PAGE_GENERIC, collection, HID_USAGE_GENERIC_Y,
|
|
|
|
&value, impl->preparsed, report_buf, report_len );
|
|
|
|
if (status != HIDP_STATUS_SUCCESS) continue;
|
|
|
|
else y = scale_value( value, impl->y_caps + i, 0, 65535 );
|
|
|
|
|
|
|
|
add_contact( impl, &old_contacts, id, x, y );
|
|
|
|
}
|
|
|
|
|
|
|
|
release_contacts( &old_contacts );
|
2024-02-13 10:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static NTSTATUS WINAPI read_completion( DEVICE_OBJECT *device, IRP *irp, void *context )
|
|
|
|
{
|
|
|
|
struct device *impl = impl_from_DEVICE_OBJECT( context );
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
TRACE( "device %p, irp %p, context %p\n", device, irp, context );
|
|
|
|
|
|
|
|
if (irp->IoStatus.Status)
|
|
|
|
WARN( "device read failed with status %#lx, stopping\n", irp->IoStatus.Status );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
process_hid_report( impl, impl->report_buf, impl->report_len );
|
|
|
|
|
|
|
|
if (!InterlockedOr( &impl->removed, FALSE ) && (status = start_device_read( context )))
|
|
|
|
ERR( "Failed to start next read, status %#lx\n", status );
|
|
|
|
}
|
|
|
|
|
|
|
|
if (irp->PendingReturned) IoMarkIrpPending( irp );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2024-01-14 14:02:37 +00:00
|
|
|
static NTSTATUS WINAPI driver_ioctl( DEVICE_OBJECT *device, IRP *irp )
|
|
|
|
{
|
|
|
|
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
|
|
|
|
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
|
|
|
|
struct device *impl = impl_from_DEVICE_OBJECT( device );
|
|
|
|
|
|
|
|
if (InterlockedOr( &impl->removed, FALSE ))
|
|
|
|
{
|
|
|
|
irp->IoStatus.Status = STATUS_DELETE_PENDING;
|
|
|
|
irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
|
|
|
return STATUS_DELETE_PENDING;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE( "device %p, irp %p, code %#lx, bus_device %p.\n", device, irp, code, impl->bus_device );
|
|
|
|
|
|
|
|
IoSkipCurrentIrpStackLocation( irp );
|
|
|
|
return IoCallDriver( impl->bus_device, irp );
|
|
|
|
}
|
|
|
|
|
2024-02-13 11:33:18 +00:00
|
|
|
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 );
|
2024-02-13 10:38:46 +00:00
|
|
|
IO_STACK_LOCATION *stack;
|
2024-02-13 11:33:18 +00:00
|
|
|
IO_STATUS_BLOCK io;
|
|
|
|
NTSTATUS status;
|
|
|
|
KEVENT event;
|
|
|
|
IRP *irp;
|
|
|
|
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
2024-02-13 10:38:46 +00:00
|
|
|
if (major == IRP_MJ_DEVICE_CONTROL)
|
|
|
|
irp = IoBuildDeviceIoControlRequest( code, device, in_buf, in_len, out_buf, out_len,
|
|
|
|
FALSE, &event, &io );
|
|
|
|
else
|
|
|
|
irp = IoBuildSynchronousFsdRequest( code, device, out_buf, out_len, NULL, &event, &io );
|
2024-02-13 11:33:18 +00:00
|
|
|
if (!irp) return STATUS_NO_MEMORY;
|
|
|
|
|
2024-02-13 10:38:46 +00:00
|
|
|
irp->Tail.Overlay.OriginalFileObject = &impl->dummy_file;
|
|
|
|
if (code == IRP_MJ_CREATE) irp->Flags |= IRP_CREATE_OPERATION;
|
|
|
|
if (code == IRP_MJ_CLOSE) irp->Flags |= IRP_CLOSE_OPERATION;
|
|
|
|
stack = IoGetNextIrpStackLocation( irp );
|
|
|
|
stack->FileObject = &impl->dummy_file;
|
|
|
|
|
2024-02-13 11:33:18 +00:00
|
|
|
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;
|
|
|
|
|
2024-02-13 10:31:37 +00:00
|
|
|
list_init( &impl->contacts );
|
|
|
|
|
2024-02-13 10:38:46 +00:00
|
|
|
if ((status = call_hid_device( device, IRP_MJ_CREATE, 0, NULL, 0, NULL, 0 ))) return status;
|
2024-02-13 11:33:18 +00:00
|
|
|
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;
|
|
|
|
|
2024-02-13 10:38:46 +00:00
|
|
|
impl->report_len = caps.InputReportByteLength;
|
|
|
|
if (!(impl->report_buf = malloc( impl->report_len ))) return STATUS_NO_MEMORY;
|
|
|
|
impl->report_buf[0] = value_caps.ReportID;
|
|
|
|
|
2024-02-13 11:33:18 +00:00
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
failed:
|
|
|
|
ERR( "%#x usage not found, unsupported device\n", usage );
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2024-01-14 14:02:37 +00:00
|
|
|
static NTSTATUS WINAPI set_event_completion( DEVICE_OBJECT *device, IRP *irp, void *context )
|
|
|
|
{
|
|
|
|
if (irp->PendingReturned) KeSetEvent( (KEVENT *)context, IO_NO_INCREMENT, FALSE );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static NTSTATUS WINAPI driver_pnp( DEVICE_OBJECT *device, IRP *irp )
|
|
|
|
{
|
|
|
|
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
|
|
|
|
struct device *impl = impl_from_DEVICE_OBJECT( device );
|
|
|
|
UCHAR code = stack->MinorFunction;
|
|
|
|
NTSTATUS status;
|
|
|
|
KEVENT event;
|
|
|
|
|
|
|
|
TRACE( "device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, impl->bus_device );
|
|
|
|
|
|
|
|
switch (stack->MinorFunction)
|
|
|
|
{
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( irp );
|
|
|
|
IoSetCompletionRoutine( irp, set_event_completion, &event, TRUE, TRUE, TRUE );
|
|
|
|
|
|
|
|
status = IoCallDriver( impl->bus_device, irp );
|
|
|
|
if (status == STATUS_PENDING)
|
|
|
|
{
|
|
|
|
KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
|
|
|
|
status = irp->IoStatus.Status;
|
|
|
|
}
|
2024-02-13 11:33:18 +00:00
|
|
|
if (!status) status = initialize_device( device );
|
2024-02-13 10:38:46 +00:00
|
|
|
if (!status) status = start_device_read( device );
|
2024-01-14 14:02:37 +00:00
|
|
|
|
|
|
|
if (status) irp->IoStatus.Status = status;
|
|
|
|
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
|
|
|
return status;
|
|
|
|
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
|
|
status = STATUS_SUCCESS;
|
2024-02-13 10:38:46 +00:00
|
|
|
if (InterlockedExchange( &impl->removed, TRUE )) break;
|
|
|
|
call_hid_device( device, IRP_MJ_CLOSE, 0, NULL, 0, NULL, 0 );
|
2024-01-14 14:02:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
IoSkipCurrentIrpStackLocation( irp );
|
|
|
|
status = IoCallDriver( impl->bus_device, irp );
|
|
|
|
IoDetachDevice( impl->bus_device );
|
2024-02-13 10:31:37 +00:00
|
|
|
release_contacts( &impl->contacts );
|
2024-02-13 11:33:18 +00:00
|
|
|
free( impl->id_caps );
|
|
|
|
free( impl->x_caps );
|
|
|
|
free( impl->y_caps );
|
2024-02-13 10:38:46 +00:00
|
|
|
free( impl->report_buf );
|
2024-02-13 11:33:18 +00:00
|
|
|
free( impl->preparsed );
|
2024-01-14 14:02:37 +00:00
|
|
|
IoDeleteDevice( device );
|
|
|
|
return status;
|
|
|
|
|
|
|
|
default:
|
|
|
|
IoSkipCurrentIrpStackLocation( irp );
|
|
|
|
return IoCallDriver( impl->bus_device, irp );
|
|
|
|
}
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static NTSTATUS WINAPI add_device( DRIVER_OBJECT *driver, DEVICE_OBJECT *bus_device )
|
|
|
|
{
|
|
|
|
struct device *impl;
|
|
|
|
DEVICE_OBJECT *device;
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
TRACE( "driver %p, bus_device %p.\n", driver, bus_device );
|
|
|
|
|
|
|
|
if ((status = IoCreateDevice( driver, sizeof(struct device), NULL, FILE_DEVICE_BUS_EXTENDER,
|
|
|
|
0, FALSE, &device )))
|
|
|
|
{
|
|
|
|
ERR( "failed to create bus FDO, status %#lx.\n", status );
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl = device->DeviceExtension;
|
|
|
|
impl->bus_device = bus_device;
|
|
|
|
|
|
|
|
IoAttachDeviceToDeviceStack( device, bus_device );
|
|
|
|
device->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WINAPI driver_unload( DRIVER_OBJECT *driver )
|
|
|
|
{
|
|
|
|
TRACE( "driver %p\n", driver );
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
|
|
|
|
{
|
|
|
|
TRACE( "driver %p, path %s.\n", driver, debugstr_w(path->Buffer) );
|
|
|
|
|
|
|
|
driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = driver_ioctl;
|
|
|
|
driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver_ioctl;
|
|
|
|
driver->MajorFunction[IRP_MJ_PNP] = driver_pnp;
|
|
|
|
driver->DriverExtension->AddDevice = add_device;
|
|
|
|
driver->DriverUnload = driver_unload;
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|