VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.cpp

Last change on this file was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: VBoxUsbTool.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Windows USB R0 Tooling.
4 */
5
6/*
7 * Copyright (C) 2011-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37#define INITGUID
38#include "VBoxUsbTool.h"
39#include <usbbusif.h>
40
41#include <iprt/assert.h>
42#include <iprt/string.h>
43#include <iprt/utf16.h>
44#include <VBox/log.h>
45#include <VBox/usblib.h>
46
47#include "../../../win/VBoxDbgLog.h"
48
49#define VBOXUSBTOOL_MEMTAG 'TUBV'
50
51static PVOID vboxUsbToolMemAlloc(SIZE_T cbBytes)
52{
53 PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXUSBTOOL_MEMTAG);
54 Assert(pvMem);
55 return pvMem;
56}
57
58static PVOID vboxUsbToolMemAllocZ(SIZE_T cbBytes)
59{
60 PVOID pvMem = vboxUsbToolMemAlloc(cbBytes);
61 if (pvMem)
62 {
63 RtlZeroMemory(pvMem, cbBytes);
64 }
65 return pvMem;
66}
67
68static VOID vboxUsbToolMemFree(PVOID pvMem)
69{
70 ExFreePoolWithTag(pvMem, VBOXUSBTOOL_MEMTAG);
71}
72
73VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAlloc(USHORT u16Function, USHORT cbSize)
74{
75 PURB pUrb = (PURB)vboxUsbToolMemAlloc(cbSize);
76 Assert(pUrb);
77 if (!pUrb)
78 return NULL;
79
80 pUrb->UrbHeader.Length = cbSize;
81 pUrb->UrbHeader.Function = u16Function;
82 return pUrb;
83}
84
85VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAllocZ(USHORT u16Function, USHORT cbSize)
86{
87 PURB pUrb = (PURB)vboxUsbToolMemAllocZ(cbSize);
88 Assert(pUrb);
89 if (!pUrb)
90 return NULL;
91
92 pUrb->UrbHeader.Length = cbSize;
93 pUrb->UrbHeader.Function = u16Function;
94 return pUrb;
95}
96
97VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbReinit(PURB pUrb, USHORT cbSize, USHORT u16Function)
98{
99 Assert(pUrb->UrbHeader.Length == cbSize);
100 if (pUrb->UrbHeader.Length < cbSize)
101 return NULL;
102 pUrb->UrbHeader.Length = cbSize;
103 pUrb->UrbHeader.Function = u16Function;
104 return pUrb;
105}
106
107VBOXUSBTOOL_DECL(VOID) VBoxUsbToolUrbFree(PURB pUrb)
108{
109 vboxUsbToolMemFree(pUrb);
110}
111
112VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolUrbPost(PDEVICE_OBJECT pDevObj, PURB pUrb, ULONG dwTimeoutMs)
113{
114 if (dwTimeoutMs == RT_INDEFINITE_WAIT)
115 return VBoxUsbToolIoInternalCtlSendSync(pDevObj, IOCTL_INTERNAL_USB_SUBMIT_URB, pUrb, NULL);
116 return VBoxUsbToolIoInternalCtlSendSyncWithTimeout(pDevObj, IOCTL_INTERNAL_USB_SUBMIT_URB, pUrb, NULL, dwTimeoutMs);
117}
118
119VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDescriptor(PDEVICE_OBJECT pDevObj, void *pvBuffer, int cbBuffer, int Type, int iIndex, int LangId, ULONG dwTimeoutMs)
120{
121 NTSTATUS Status;
122 USHORT cbUrb = sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST);
123 PURB pUrb = VBoxUsbToolUrbAllocZ(URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, cbUrb);
124 if (!pUrb)
125 {
126 WARN(("allocating URB failed"));
127 return STATUS_INSUFFICIENT_RESOURCES;
128 }
129
130 PUSB_COMMON_DESCRIPTOR pCmn = (PUSB_COMMON_DESCRIPTOR)pvBuffer;
131 pCmn->bLength = cbBuffer;
132 pCmn->bDescriptorType = Type;
133
134 pUrb->UrbHeader.Function = URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE;
135 pUrb->UrbHeader.Length = sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST);
136 pUrb->UrbControlDescriptorRequest.TransferBufferLength = cbBuffer;
137 pUrb->UrbControlDescriptorRequest.TransferBuffer = pvBuffer;
138 pUrb->UrbControlDescriptorRequest.Index = (UCHAR)iIndex;
139 pUrb->UrbControlDescriptorRequest.DescriptorType = (UCHAR)Type;
140 pUrb->UrbControlDescriptorRequest.LanguageId = (USHORT)LangId;
141
142 Status = VBoxUsbToolUrbPost(pDevObj, pUrb, dwTimeoutMs);
143 ASSERT_WARN(Status == STATUS_SUCCESS, ("VBoxUsbToolUrbPost failed Status (0x%x)", Status));
144
145 VBoxUsbToolUrbFree(pUrb);
146
147 return Status;
148}
149
150VBOXUSBTOOL_DECL(VOID) VBoxUsbToolStringDescriptorToUnicodeString(PUSB_STRING_DESCRIPTOR pDr, PUNICODE_STRING pUnicode)
151{
152 /* for some reason the string dr sometimes contains a non-null terminated string
153 * although we zeroed up the complete descriptor buffer
154 * this is why RtlInitUnicodeString won't work
155 * we need to init the scting length based on dr length */
156 pUnicode->Buffer = pDr->bString;
157 pUnicode->Length = pUnicode->MaximumLength = pDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString);
158}
159
160VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetStringDescriptor(PDEVICE_OBJECT pDevObj, char *pszResult, ULONG cbResult,
161 int iIndex, int LangId, ULONG dwTimeoutMs)
162{
163 char aBuf[MAXIMUM_USB_STRING_LENGTH];
164 AssertCompile(sizeof (aBuf) <= UINT8_MAX);
165 UCHAR cbBuf = (UCHAR)sizeof (aBuf);
166 PUSB_STRING_DESCRIPTOR pDr = (PUSB_STRING_DESCRIPTOR)&aBuf;
167
168 Assert(pszResult);
169 *pszResult = 0;
170
171 memset(pDr, 0, cbBuf);
172 pDr->bLength = cbBuf;
173 pDr->bDescriptorType = USB_STRING_DESCRIPTOR_TYPE;
174
175 NTSTATUS Status = VBoxUsbToolGetDescriptor(pDevObj, pDr, cbBuf, USB_STRING_DESCRIPTOR_TYPE, iIndex, LangId, dwTimeoutMs);
176 if (NT_SUCCESS(Status))
177 {
178 if (pDr->bLength >= sizeof (USB_STRING_DESCRIPTOR))
179 {
180 int rc = RTUtf16ToUtf8Ex(pDr->bString, (pDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString)) / sizeof(RTUTF16),
181 &pszResult, cbResult, NULL /*pcch*/);
182 if (RT_SUCCESS(rc))
183 {
184 USBLibPurgeEncoding(pszResult);
185 Status = STATUS_SUCCESS;
186 }
187 else
188 Status = STATUS_UNSUCCESSFUL;
189 }
190 else
191 Status = STATUS_INVALID_PARAMETER;
192 }
193 return Status;
194}
195
196VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetLangID(PDEVICE_OBJECT pDevObj, int *pLangId, ULONG dwTimeoutMs)
197{
198 char aBuf[MAXIMUM_USB_STRING_LENGTH];
199 AssertCompile(sizeof (aBuf) <= UINT8_MAX);
200 UCHAR cbBuf = (UCHAR)sizeof (aBuf);
201 PUSB_STRING_DESCRIPTOR pDr = (PUSB_STRING_DESCRIPTOR)&aBuf;
202
203 Assert(pLangId);
204 *pLangId = 0;
205
206 memset(pDr, 0, cbBuf);
207 pDr->bLength = cbBuf;
208 pDr->bDescriptorType = USB_STRING_DESCRIPTOR_TYPE;
209
210 NTSTATUS Status = VBoxUsbToolGetDescriptor(pDevObj, pDr, cbBuf, USB_STRING_DESCRIPTOR_TYPE, 0, 0, dwTimeoutMs);
211 if (NT_SUCCESS(Status))
212 {
213 /* Just grab the first lang ID if available. In 99% cases, it will be US English (0x0409).*/
214 if (pDr->bLength >= sizeof (USB_STRING_DESCRIPTOR))
215 {
216 AssertCompile(sizeof (pDr->bString[0]) == sizeof (uint16_t));
217 *pLangId = pDr->bString[0];
218 Status = STATUS_SUCCESS;
219 }
220 else
221 {
222 Status = STATUS_INVALID_PARAMETER;
223 }
224 }
225 return Status;
226}
227
228VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDeviceSpeed(PDEVICE_OBJECT pDevObj, BOOLEAN *pbIsHigh)
229{
230 Assert(pbIsHigh);
231 *pbIsHigh = FALSE;
232
233 PIRP pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
234 Assert(pIrp);
235 if (!pIrp)
236 {
237 return STATUS_INSUFFICIENT_RESOURCES;
238 }
239
240 USB_BUS_INTERFACE_USBDI_V1 BusIf;
241 PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
242 pSl->MajorFunction = IRP_MJ_PNP;
243 pSl->MinorFunction = IRP_MN_QUERY_INTERFACE;
244 pSl->Parameters.QueryInterface.InterfaceType = &USB_BUS_INTERFACE_USBDI_GUID;
245 pSl->Parameters.QueryInterface.Size = sizeof (BusIf);
246 pSl->Parameters.QueryInterface.Version = USB_BUSIF_USBDI_VERSION_1;
247 pSl->Parameters.QueryInterface.Interface = (PINTERFACE)&BusIf;
248 pSl->Parameters.QueryInterface.InterfaceSpecificData = NULL;
249
250 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
251
252 NTSTATUS Status = VBoxDrvToolIoPostSync(pDevObj, pIrp);
253 Assert(NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED);
254 if (NT_SUCCESS(Status))
255 {
256 *pbIsHigh = BusIf.IsDeviceHighSpeed(BusIf.BusContext);
257 BusIf.InterfaceDereference(BusIf.BusContext);
258 }
259 IoFreeIrp(pIrp);
260
261 return Status;
262}
263
264VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolPipeClear(PDEVICE_OBJECT pDevObj, HANDLE hPipe, bool fReset)
265{
266 if (!hPipe)
267 {
268 Log(("Resetting the control pipe??\n"));
269 return STATUS_SUCCESS;
270 }
271 USHORT u16Function = fReset ? URB_FUNCTION_RESET_PIPE : URB_FUNCTION_ABORT_PIPE;
272 PURB pUrb = VBoxUsbToolUrbAlloc(u16Function, sizeof (struct _URB_PIPE_REQUEST));
273 if (!pUrb)
274 {
275 AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbAlloc failed!\n"));
276 return STATUS_INSUFFICIENT_RESOURCES;
277 }
278 pUrb->UrbPipeRequest.PipeHandle = hPipe;
279 pUrb->UrbPipeRequest.Reserved = 0;
280
281 NTSTATUS Status = VBoxUsbToolUrbPost(pDevObj, pUrb, RT_INDEFINITE_WAIT);
282 if (!NT_SUCCESS(Status) || !USBD_SUCCESS(pUrb->UrbHeader.Status))
283 {
284 AssertMsgFailed((__FUNCTION__": vboxUsbToolRequest failed with %x (%x)\n", Status, pUrb->UrbHeader.Status));
285 }
286
287 VBoxUsbToolUrbFree(pUrb);
288
289 return Status;
290}
291
292VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolCurrentFrame(PDEVICE_OBJECT pDevObj, PIRP pIrp, PULONG piFrame)
293{
294 struct _URB_GET_CURRENT_FRAME_NUMBER Urb;
295 Urb.Hdr.Function = URB_FUNCTION_GET_CURRENT_FRAME_NUMBER;
296 Urb.Hdr.Length = sizeof(Urb);
297 Urb.FrameNumber = (ULONG)-1;
298
299 Assert(piFrame);
300 *piFrame = (ULONG)-1;
301
302 PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
303 pSl->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
304 pSl->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
305 pSl->Parameters.Others.Argument1 = (PVOID)&Urb;
306 pSl->Parameters.Others.Argument2 = NULL;
307
308 NTSTATUS Status = VBoxUsbToolUrbPost(pDevObj, (PURB)&Urb, RT_INDEFINITE_WAIT);
309 Assert(NT_SUCCESS(Status));
310 if (NT_SUCCESS(Status))
311 {
312 *piFrame = Urb.FrameNumber;
313 }
314
315 return Status;
316}
317
318VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolDevUnconfigure(PDEVICE_OBJECT pDevObj)
319{
320 USHORT cbUrb = sizeof (struct _URB_SELECT_CONFIGURATION);
321 PURB pUrb = VBoxUsbToolUrbAlloc(URB_FUNCTION_SELECT_CONFIGURATION, cbUrb);
322 Assert(pUrb);
323 if (!pUrb)
324 return STATUS_INSUFFICIENT_RESOURCES;
325
326 UsbBuildSelectConfigurationRequest(pUrb, (USHORT)cbUrb, NULL);
327
328 NTSTATUS Status = VBoxUsbToolUrbPost(pDevObj, pUrb, RT_INDEFINITE_WAIT);
329 Assert(NT_SUCCESS(Status));
330
331 VBoxUsbToolUrbFree(pUrb);
332
333 return Status;
334}
335
336VBOXUSBTOOL_DECL(PIRP) VBoxUsbToolIoBuildAsyncInternalCtl(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2)
337{
338 PIRP pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
339 Assert(pIrp);
340 if (!pIrp)
341 {
342 return NULL;
343 }
344
345 pIrp->IoStatus.Status = STATUS_SUCCESS;
346 pIrp->IoStatus.Information = NULL;
347
348 PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
349 pSl->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
350 pSl->MinorFunction = 0;
351 pSl->Parameters.DeviceIoControl.IoControlCode = uCtl;
352 pSl->Parameters.Others.Argument1 = pvArg1;
353 pSl->Parameters.Others.Argument2 = pvArg2;
354 return pIrp;
355}
356
357VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSyncWithTimeout(PDEVICE_OBJECT pDevObj, ULONG uCtl,
358 void *pvArg1, void *pvArg2, ULONG dwTimeoutMs)
359{
360 /* since we're going to cancel the irp on timeout, we should allocate our own IRP rather than using the threaded one
361 * */
362 PIRP pIrp = VBoxUsbToolIoBuildAsyncInternalCtl(pDevObj, uCtl, pvArg1, pvArg2);
363 if (!pIrp)
364 {
365 WARN(("VBoxUsbToolIoBuildAsyncInternalCtl failed"));
366 return STATUS_INSUFFICIENT_RESOURCES;
367 }
368
369 NTSTATUS Status = VBoxDrvToolIoPostSyncWithTimeout(pDevObj, pIrp, dwTimeoutMs);
370
371 IoFreeIrp(pIrp);
372
373 return Status;
374}
375
376VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendAsync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2,
377 PKEVENT pEvent, PIO_STATUS_BLOCK pIoStatus)
378{
379 NTSTATUS Status;
380 PIRP pIrp;
381 PIO_STACK_LOCATION pSl;
382 Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
383
384 pIrp = IoBuildDeviceIoControlRequest(uCtl, pDevObj, NULL, 0, NULL, 0, TRUE, pEvent, pIoStatus);
385 if (!pIrp)
386 {
387 WARN(("IoBuildDeviceIoControlRequest failed!!\n"));
388 pIoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
389 pIoStatus->Information = 0;
390 return STATUS_INSUFFICIENT_RESOURCES;
391 }
392
393 /* Get the next stack location as that is used for the new irp */
394 pSl = IoGetNextIrpStackLocation(pIrp);
395 pSl->Parameters.Others.Argument1 = pvArg1;
396 pSl->Parameters.Others.Argument2 = pvArg2;
397
398 Status = IoCallDriver(pDevObj, pIrp);
399
400 return Status;
401}
402
403VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2)
404{
405 IO_STATUS_BLOCK IoStatus = {0};
406 KEVENT Event;
407 NTSTATUS Status;
408
409 KeInitializeEvent(&Event, NotificationEvent, FALSE);
410
411 LOG(("Sending sync Ctl pDevObj(0x%p), uCtl(0x%x), pvArg1(0x%p), pvArg2(0x%p)", pDevObj, uCtl, pvArg1, pvArg2));
412
413 Status = VBoxUsbToolIoInternalCtlSendAsync(pDevObj, uCtl, pvArg1, pvArg2, &Event, &IoStatus);
414
415 if (Status == STATUS_PENDING)
416 {
417 LOG(("VBoxUsbToolIoInternalCtlSendAsync returned pending for pDevObj(0x%p)", pDevObj));
418 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
419 Status = IoStatus.Status;
420 LOG(("Pending VBoxUsbToolIoInternalCtlSendAsync completed with Status (0x%x) for pDevObj(0x%p)", Status, pDevObj));
421 }
422 else
423 {
424 LOG(("VBoxUsbToolIoInternalCtlSendAsync completed with Status (0x%x) for pDevObj(0x%p)", Status, pDevObj));
425 }
426
427 return Status;
428}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use