2 * USB root device enumerator using libusb
4 * Copyright 2020 Zebediah Figura
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #define WIN32_NO_STATUS
34 #include "ddk/usbioctl.h"
36 #include "wine/debug.h"
37 #include "wine/list.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(wineusb
);
43 #ifdef __ASM_USE_FASTCALL_WRAPPER
45 extern void * WINAPI
wrap_fastcall_func1( void *func
, const void *a
);
46 __ASM_STDCALL_FUNC( wrap_fastcall_func1
, 8,
49 "xchgl (%esp),%ecx\n\t"
52 #define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
56 #define call_fastcall_func1(func,a) func(a)
60 #define DECLARE_CRITICAL_SECTION(cs) \
61 static CRITICAL_SECTION cs; \
62 static CRITICAL_SECTION_DEBUG cs##_debug = \
63 { 0, 0, &cs, { &cs##_debug.ProcessLocksList, &cs##_debug.ProcessLocksList }, \
64 0, 0, { (DWORD_PTR)(__FILE__ ": " # cs) }}; \
65 static CRITICAL_SECTION cs = { &cs##_debug, -1, 0, 0, 0, 0 };
67 DECLARE_CRITICAL_SECTION(wineusb_cs
);
69 static struct list device_list
= LIST_INIT(device_list
);
76 DEVICE_OBJECT
*device_obj
;
79 int16_t interface_index
;
81 uint8_t class, subclass
, protocol
, busnum
, portnum
;
83 uint16_t vendor
, product
, revision
, usbver
;
85 struct unix_device
*unix_device
;
90 static DRIVER_OBJECT
*driver_obj
;
91 static DEVICE_OBJECT
*bus_fdo
, *bus_pdo
;
93 static void destroy_unix_device(struct unix_device
*unix_device
)
95 struct usb_destroy_device_params params
=
97 .device
= unix_device
,
100 WINE_UNIX_CALL(unix_usb_destroy_device
, ¶ms
);
103 static void add_unix_device(const struct usb_add_device_event
*event
)
105 static unsigned int name_index
;
106 struct usb_device
*device
;
107 DEVICE_OBJECT
*device_obj
;
108 UNICODE_STRING string
;
112 TRACE("Adding new device %p, vendor %04x, product %04x.\n", event
->device
,
113 event
->vendor
, event
->product
);
115 swprintf(name
, ARRAY_SIZE(name
), L
"\\Device\\USBPDO-%u", name_index
++);
116 RtlInitUnicodeString(&string
, name
);
117 if ((status
= IoCreateDevice(driver_obj
, sizeof(*device
), &string
,
118 FILE_DEVICE_USB
, 0, FALSE
, &device_obj
)))
120 ERR("Failed to create device, status %#lx.\n", status
);
124 device
= device_obj
->DeviceExtension
;
125 device
->device_obj
= device_obj
;
126 device
->unix_device
= event
->device
;
127 InitializeListHead(&device
->irp_list
);
128 device
->removed
= FALSE
;
130 device
->interface
= event
->interface
;
131 device
->interface_index
= event
->interface_index
;
133 device
->class = event
->class;
134 device
->subclass
= event
->subclass
;
135 device
->protocol
= event
->protocol
;
136 device
->busnum
= event
->busnum
;
137 device
->portnum
= event
->portnum
;
139 device
->vendor
= event
->vendor
;
140 device
->product
= event
->product
;
141 device
->revision
= event
->revision
;
142 device
->usbver
= event
->usbver
;
144 EnterCriticalSection(&wineusb_cs
);
145 list_add_tail(&device_list
, &device
->entry
);
146 LeaveCriticalSection(&wineusb_cs
);
148 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
151 static void remove_unix_device(struct unix_device
*unix_device
)
153 struct usb_device
*device
;
155 TRACE("Removing device %p.\n", unix_device
);
157 EnterCriticalSection(&wineusb_cs
);
158 LIST_FOR_EACH_ENTRY(device
, &device_list
, struct usb_device
, entry
)
160 if (device
->unix_device
== unix_device
)
162 if (!device
->removed
)
164 device
->removed
= TRUE
;
165 list_remove(&device
->entry
);
170 LeaveCriticalSection(&wineusb_cs
);
172 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
175 static HANDLE event_thread
;
177 static void complete_irp(IRP
*irp
)
179 EnterCriticalSection(&wineusb_cs
);
180 RemoveEntryList(&irp
->Tail
.Overlay
.ListEntry
);
181 LeaveCriticalSection(&wineusb_cs
);
183 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
184 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
187 static DWORD CALLBACK
event_thread_proc(void *arg
)
189 struct usb_event event
;
190 struct usb_main_loop_params params
=
195 TRACE("Starting event thread.\n");
197 if (WINE_UNIX_CALL(unix_usb_init
, NULL
) != STATUS_SUCCESS
)
200 while (WINE_UNIX_CALL(unix_usb_main_loop
, ¶ms
) == STATUS_PENDING
)
204 case USB_EVENT_ADD_DEVICE
:
205 add_unix_device(&event
.u
.added_device
);
208 case USB_EVENT_REMOVE_DEVICE
:
209 remove_unix_device(event
.u
.removed_device
);
212 case USB_EVENT_TRANSFER_COMPLETE
:
213 complete_irp(event
.u
.completed_irp
);
218 TRACE("Shutting down event thread.\n");
222 static NTSTATUS
fdo_pnp(IRP
*irp
)
224 IO_STACK_LOCATION
*stack
= IoGetCurrentIrpStackLocation(irp
);
227 TRACE("irp %p, minor function %#x.\n", irp
, stack
->MinorFunction
);
229 switch (stack
->MinorFunction
)
231 case IRP_MN_QUERY_DEVICE_RELATIONS
:
233 struct usb_device
*device
;
234 DEVICE_RELATIONS
*devices
;
237 if (stack
->Parameters
.QueryDeviceRelations
.Type
!= BusRelations
)
239 FIXME("Unhandled device relations type %#x.\n", stack
->Parameters
.QueryDeviceRelations
.Type
);
243 EnterCriticalSection(&wineusb_cs
);
245 if (!(devices
= ExAllocatePool(PagedPool
,
246 offsetof(DEVICE_RELATIONS
, Objects
[list_count(&device_list
)]))))
248 LeaveCriticalSection(&wineusb_cs
);
249 irp
->IoStatus
.Status
= STATUS_NO_MEMORY
;
253 LIST_FOR_EACH_ENTRY(device
, &device_list
, struct usb_device
, entry
)
255 devices
->Objects
[i
++] = device
->device_obj
;
256 call_fastcall_func1(ObfReferenceObject
, device
->device_obj
);
259 LeaveCriticalSection(&wineusb_cs
);
262 irp
->IoStatus
.Information
= (ULONG_PTR
)devices
;
263 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
267 case IRP_MN_START_DEVICE
:
268 event_thread
= CreateThread(NULL
, 0, event_thread_proc
, NULL
, 0, NULL
);
270 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
273 case IRP_MN_SURPRISE_REMOVAL
:
274 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
277 case IRP_MN_REMOVE_DEVICE
:
279 struct usb_device
*device
, *cursor
;
281 WINE_UNIX_CALL(unix_usb_exit
, NULL
);
282 WaitForSingleObject(event_thread
, INFINITE
);
283 CloseHandle(event_thread
);
285 EnterCriticalSection(&wineusb_cs
);
286 /* Normally we unlink all devices either:
288 * - as a result of hot-unplug, which unlinks the device, and causes
289 * a subsequent IRP_MN_REMOVE_DEVICE which will free it;
291 * - if the parent is deleted (at shutdown time), in which case
292 * ntoskrnl will send us IRP_MN_SURPRISE_REMOVAL and
293 * IRP_MN_REMOVE_DEVICE unprompted.
295 * But we can get devices hotplugged between when shutdown starts
296 * and now, in which case they'll be stuck in this list and never
299 * FIXME: This is still broken, though. If a device is hotplugged
300 * and then removed, it'll be unlinked and never freed. */
301 LIST_FOR_EACH_ENTRY_SAFE(device
, cursor
, &device_list
, struct usb_device
, entry
)
303 assert(!device
->removed
);
304 destroy_unix_device(device
->unix_device
);
305 list_remove(&device
->entry
);
306 IoDeleteDevice(device
->device_obj
);
308 LeaveCriticalSection(&wineusb_cs
);
310 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
311 IoSkipCurrentIrpStackLocation(irp
);
312 ret
= IoCallDriver(bus_pdo
, irp
);
313 IoDetachDevice(bus_pdo
);
314 IoDeleteDevice(bus_fdo
);
319 FIXME("Unhandled minor function %#x.\n", stack
->MinorFunction
);
322 IoSkipCurrentIrpStackLocation(irp
);
323 return IoCallDriver(bus_pdo
, irp
);
332 static void WINAPIV
append_id(struct string_buffer
*buffer
, const WCHAR
*format
, ...)
338 va_start(args
, format
);
340 len
= _vsnwprintf(NULL
, 0, format
, args
) + 1;
341 if (!(string
= ExAllocatePool(PagedPool
, (buffer
->len
+ len
) * sizeof(WCHAR
))))
344 ExFreePool(buffer
->string
);
345 buffer
->string
= NULL
;
350 memcpy(string
, buffer
->string
, buffer
->len
* sizeof(WCHAR
));
351 ExFreePool(buffer
->string
);
353 _vsnwprintf(string
+ buffer
->len
, len
, format
, args
);
354 buffer
->string
= string
;
360 static void get_device_id(const struct usb_device
*device
, struct string_buffer
*buffer
)
362 if (device
->interface
)
363 append_id(buffer
, L
"USB\\VID_%04X&PID_%04X&MI_%02X",
364 device
->vendor
, device
->product
, device
->interface_index
);
366 append_id(buffer
, L
"USB\\VID_%04X&PID_%04X", device
->vendor
, device
->product
);
369 static void get_instance_id(const struct usb_device
*device
, struct string_buffer
*buffer
)
371 append_id(buffer
, L
"%u&%u&%u&%u", device
->usbver
, device
->revision
, device
->busnum
, device
->portnum
);
374 static void get_hardware_ids(const struct usb_device
*device
, struct string_buffer
*buffer
)
376 if (device
->interface
)
377 append_id(buffer
, L
"USB\\VID_%04X&PID_%04X&REV_%04X&MI_%02X",
378 device
->vendor
, device
->product
, device
->revision
, device
->interface_index
);
380 append_id(buffer
, L
"USB\\VID_%04X&PID_%04X&REV_%04X",
381 device
->vendor
, device
->product
, device
->revision
);
383 get_device_id(device
, buffer
);
384 append_id(buffer
, L
"");
387 static void get_compatible_ids(const struct usb_device
*device
, struct string_buffer
*buffer
)
389 if (device
->interface_index
!= -1)
391 append_id(buffer
, L
"USB\\Class_%02x&SubClass_%02x&Prot_%02x",
392 device
->class, device
->subclass
, device
->protocol
);
393 append_id(buffer
, L
"USB\\Class_%02x&SubClass_%02x", device
->class, device
->subclass
);
394 append_id(buffer
, L
"USB\\Class_%02x", device
->class);
398 append_id(buffer
, L
"USB\\DevClass_%02x&SubClass_%02x&Prot_%02x",
399 device
->class, device
->subclass
, device
->protocol
);
400 append_id(buffer
, L
"USB\\DevClass_%02x&SubClass_%02x", device
->class, device
->subclass
);
401 append_id(buffer
, L
"USB\\DevClass_%02x", device
->class);
403 append_id(buffer
, L
"");
406 static NTSTATUS
query_id(struct usb_device
*device
, IRP
*irp
, BUS_QUERY_ID_TYPE type
)
408 struct string_buffer buffer
= {0};
410 TRACE("type %#x.\n", type
);
414 case BusQueryDeviceID
:
415 get_device_id(device
, &buffer
);
418 case BusQueryInstanceID
:
419 get_instance_id(device
, &buffer
);
422 case BusQueryHardwareIDs
:
423 get_hardware_ids(device
, &buffer
);
426 case BusQueryCompatibleIDs
:
427 get_compatible_ids(device
, &buffer
);
431 FIXME("Unhandled ID query type %#x.\n", type
);
432 return irp
->IoStatus
.Status
;
436 return STATUS_NO_MEMORY
;
438 irp
->IoStatus
.Information
= (ULONG_PTR
)buffer
.string
;
439 return STATUS_SUCCESS
;
442 static void remove_pending_irps(struct usb_device
*device
)
447 while ((entry
= RemoveHeadList(&device
->irp_list
)) != &device
->irp_list
)
449 irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.ListEntry
);
450 irp
->IoStatus
.Status
= STATUS_DELETE_PENDING
;
451 irp
->IoStatus
.Information
= 0;
452 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
456 static NTSTATUS
pdo_pnp(DEVICE_OBJECT
*device_obj
, IRP
*irp
)
458 IO_STACK_LOCATION
*stack
= IoGetCurrentIrpStackLocation(irp
);
459 struct usb_device
*device
= device_obj
->DeviceExtension
;
460 NTSTATUS ret
= irp
->IoStatus
.Status
;
462 TRACE("device_obj %p, irp %p, minor function %#x.\n", device_obj
, irp
, stack
->MinorFunction
);
464 switch (stack
->MinorFunction
)
466 case IRP_MN_QUERY_ID
:
467 ret
= query_id(device
, irp
, stack
->Parameters
.QueryId
.IdType
);
470 case IRP_MN_QUERY_CAPABILITIES
:
472 DEVICE_CAPABILITIES
*caps
= stack
->Parameters
.DeviceCapabilities
.Capabilities
;
474 caps
->RawDeviceOK
= 1;
476 ret
= STATUS_SUCCESS
;
480 case IRP_MN_START_DEVICE
:
481 ret
= STATUS_SUCCESS
;
484 case IRP_MN_SURPRISE_REMOVAL
:
485 EnterCriticalSection(&wineusb_cs
);
486 remove_pending_irps(device
);
487 if (!device
->removed
)
489 device
->removed
= TRUE
;
490 list_remove(&device
->entry
);
492 LeaveCriticalSection(&wineusb_cs
);
493 ret
= STATUS_SUCCESS
;
496 case IRP_MN_REMOVE_DEVICE
:
497 assert(device
->removed
);
498 remove_pending_irps(device
);
500 destroy_unix_device(device
->unix_device
);
502 IoDeleteDevice(device
->device_obj
);
503 ret
= STATUS_SUCCESS
;
507 FIXME("Unhandled minor function %#x.\n", stack
->MinorFunction
);
510 irp
->IoStatus
.Status
= ret
;
511 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
515 static NTSTATUS WINAPI
driver_pnp(DEVICE_OBJECT
*device
, IRP
*irp
)
517 if (device
== bus_fdo
)
519 return pdo_pnp(device
, irp
);
522 static NTSTATUS
usb_submit_urb(struct usb_device
*device
, IRP
*irp
)
524 URB
*urb
= IoGetCurrentIrpStackLocation(irp
)->Parameters
.Others
.Argument1
;
527 TRACE("type %#x.\n", urb
->UrbHeader
.Function
);
529 switch (urb
->UrbHeader
.Function
)
531 case URB_FUNCTION_ABORT_PIPE
:
533 LIST_ENTRY
*entry
, *mark
;
535 /* The documentation states that URB_FUNCTION_ABORT_PIPE may
536 * complete before outstanding requests complete, so we don't need
537 * to wait for them. */
538 EnterCriticalSection(&wineusb_cs
);
539 mark
= &device
->irp_list
;
540 for (entry
= mark
->Flink
; entry
!= mark
; entry
= entry
->Flink
)
542 IRP
*queued_irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.ListEntry
);
543 struct usb_cancel_transfer_params params
=
545 .transfer
= queued_irp
->Tail
.Overlay
.DriverContext
[0],
548 WINE_UNIX_CALL(unix_usb_cancel_transfer
, ¶ms
);
550 LeaveCriticalSection(&wineusb_cs
);
552 return STATUS_SUCCESS
;
555 case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL
:
556 case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER
:
557 case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE
:
558 case URB_FUNCTION_SELECT_CONFIGURATION
:
559 case URB_FUNCTION_VENDOR_INTERFACE
:
561 struct usb_submit_urb_params params
=
563 .device
= device
->unix_device
,
567 switch (urb
->UrbHeader
.Function
)
569 case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER
:
571 struct _URB_BULK_OR_INTERRUPT_TRANSFER
*req
= &urb
->UrbBulkOrInterruptTransfer
;
572 if (req
->TransferBufferMDL
)
573 params
.transfer_buffer
= MmGetSystemAddressForMdlSafe(req
->TransferBufferMDL
, NormalPagePriority
);
575 params
.transfer_buffer
= req
->TransferBuffer
;
579 case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE
:
581 struct _URB_CONTROL_DESCRIPTOR_REQUEST
*req
= &urb
->UrbControlDescriptorRequest
;
582 if (req
->TransferBufferMDL
)
583 params
.transfer_buffer
= MmGetSystemAddressForMdlSafe(req
->TransferBufferMDL
, NormalPagePriority
);
585 params
.transfer_buffer
= req
->TransferBuffer
;
589 case URB_FUNCTION_VENDOR_INTERFACE
:
591 struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST
*req
= &urb
->UrbControlVendorClassRequest
;
592 if (req
->TransferBufferMDL
)
593 params
.transfer_buffer
= MmGetSystemAddressForMdlSafe(req
->TransferBufferMDL
, NormalPagePriority
);
595 params
.transfer_buffer
= req
->TransferBuffer
;
600 /* Hold the wineusb lock while submitting and queuing, and
601 * similarly hold it in complete_irp(). That way, if libusb reports
602 * completion between submitting and queuing, we won't try to
603 * dequeue the IRP until it's actually been queued. */
604 EnterCriticalSection(&wineusb_cs
);
605 status
= WINE_UNIX_CALL(unix_usb_submit_urb
, ¶ms
);
606 if (status
== STATUS_PENDING
)
608 IoMarkIrpPending(irp
);
609 InsertTailList(&device
->irp_list
, &irp
->Tail
.Overlay
.ListEntry
);
611 LeaveCriticalSection(&wineusb_cs
);
617 FIXME("Unhandled function %#x.\n", urb
->UrbHeader
.Function
);
620 return STATUS_NOT_IMPLEMENTED
;
623 static NTSTATUS WINAPI
driver_internal_ioctl(DEVICE_OBJECT
*device_obj
, IRP
*irp
)
625 IO_STACK_LOCATION
*stack
= IoGetCurrentIrpStackLocation(irp
);
626 ULONG code
= stack
->Parameters
.DeviceIoControl
.IoControlCode
;
627 struct usb_device
*device
= device_obj
->DeviceExtension
;
628 NTSTATUS status
= STATUS_NOT_IMPLEMENTED
;
631 TRACE("device_obj %p, irp %p, code %#lx.\n", device_obj
, irp
, code
);
633 EnterCriticalSection(&wineusb_cs
);
634 removed
= device
->removed
;
635 LeaveCriticalSection(&wineusb_cs
);
639 irp
->IoStatus
.Status
= STATUS_DELETE_PENDING
;
640 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
641 return STATUS_DELETE_PENDING
;
646 case IOCTL_INTERNAL_USB_SUBMIT_URB
:
647 status
= usb_submit_urb(device
, irp
);
651 FIXME("Unhandled ioctl %#lx (device %#lx, access %#lx, function %#lx, method %#lx).\n",
652 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
655 if (status
!= STATUS_PENDING
)
657 irp
->IoStatus
.Status
= status
;
658 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
663 static NTSTATUS WINAPI
driver_add_device(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*pdo
)
667 TRACE("driver %p, pdo %p.\n", driver
, pdo
);
669 if ((ret
= IoCreateDevice(driver
, 0, NULL
, FILE_DEVICE_BUS_EXTENDER
, 0, FALSE
, &bus_fdo
)))
671 ERR("Failed to create FDO, status %#lx.\n", ret
);
675 IoAttachDeviceToDeviceStack(bus_fdo
, pdo
);
677 bus_fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
679 return STATUS_SUCCESS
;
682 static void WINAPI
driver_unload(DRIVER_OBJECT
*driver
)
686 NTSTATUS WINAPI
DriverEntry(DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
690 TRACE("driver %p, path %s.\n", driver
, debugstr_w(path
->Buffer
));
692 if ((status
= __wine_init_unix_call()))
694 ERR("Failed to initialize Unix library, status %#lx.\n", status
);
700 driver
->DriverExtension
->AddDevice
= driver_add_device
;
701 driver
->DriverUnload
= driver_unload
;
702 driver
->MajorFunction
[IRP_MJ_PNP
] = driver_pnp
;
703 driver
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] = driver_internal_ioctl
;
705 return STATUS_SUCCESS
;