How to Restart a USB Port





5.00/5 (20 votes)
Shows how to use IOCTL_USB_HUB_CYCLE_PORT to restart a USB port under Windows
↵
Introduction
Sometimes, USB devices stop working as expected and re-plugging them helps. Performing the re-plug by code might be handy.
If you want to try if restarting a USB port is helpful, you can try with my UsbTreeView or RestartUsbPort.
Background
Microsoft says IOCTL_USB_HUB_CYCLE_PORT power-cycles the USB port and thus initiates a re-enumeration of the attached device.
The function is available under XP but usually only for hubs which run with the Microsoft standard driver. Third party drivers usually return ERROR_UNKNOWN_FUNCTION
.
Under Vista and Windows 7, it is no more supported by the Windows standard drivers, it always fails with ERROR_NOT_SUPPORTED
.
Since Windows 8, it works again but in contrast to XP, admin privileges are required. Without admin privileges, it fails as under Vista and Win7 with ERROR_NOT_SUPPORTED
or since Win10 version 1903 with ERROR_GEN_FAILURE
, both of which are quite misleading here since it's a privilege issue.
Using the Code
I have put everything in one single function which expects the USB device's Device Instance ID as only parameter.
//--------------------------------------------------------------------------------
// pszUsbDeviceId must be the Device Instance ID of the USB device to restart ----
// returns true on success, false on any failure ---------------------------------
//--------------------------------------------------------------------------------
bool CycleUsbDevice(char* pszUsbDeviceId)
{
// Step 1: Find the USB device in the device manager
DEVINST DevInst = 0;
if ( CR_SUCCESS != CM_Locate_DevNode(&DevInst, pszUsbDeviceId, 0) ) {
return false; //----
}
// Step 2: Determine the USB port number.
// Since Vista it can be read reliably from the device location info:
char szLocation[64] = "";
DWORD dwType = 0;
ULONG uLen = sizeof(szLocation);
if ( CR_SUCCESS != CM_Get_DevNode_Registry_Property(
DevInst, CM_DRP_LOCATION_INFORMATION,
&dwType, szLocation, &uLen, 0) ) {
return false; //----
}
// 0123456789
// like "Port_#0004.Hub_#0014"
if ( 0 != strncmp(szLocation, "Port_#", 6) ) {
return false; //----
}
int PortNumber = atoi(szLocation + 6); // leading zeros are ok with atoi
// Step 3: The USB hub is the parent device
DEVINST DevInstHub = 0;
if ( CR_SUCCESS != CM_Get_Parent(&DevInstHub, DevInst, 0) ) {
return false; //----
}
// Step 4: Request the USB hub's "device interface" aka the "DevicePath"
char szHubDevPath[MAX_PATH] = "";
// Get the hub's device instance ID
char szHubDeviceID[MAX_DEVICE_ID_LEN];
if ( CR_SUCCESS != CM_Get_Device_ID(
DevInstHub,
szHubDeviceID, MAX_DEVICE_ID_LEN, 0) ) {
return false; //----
}
// Get the hub's device path (no need for CM_Get_Device_Interface_List_Size
// since we request a single one rather a list of unknown length)
if ( CR_SUCCESS != CM_Get_Device_Interface_List(
(GUID*)&GUID_DEVINTERFACE_USB_HUB, szHubDeviceID,
szHubDevPath, sizeof(szHubDevPath),
CM_GET_DEVICE_INTERFACE_LIST_PRESENT) ) {
return false; //----
}
if ( ! szHubDevPath[0] ) {
return false; //----
}
// Step 5: Open the hub
HANDLE hHub = CreateFile(
szHubDevPath,
GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if ( INVALID_HANDLE_VALUE == hHub ) {
return false; //----
}
// Step 6: Call IOCTL_USB_HUB_CYCLE_PORT
typedef struct _USB_CYCLE_PORT_PARAMS {
ULONG ConnectionIndex;
ULONG StatusReturned;
} USB_CYCLE_PORT_PARAMS, *PUSB_CYCLE_PORT_PARAMS;
USB_CYCLE_PORT_PARAMS CyclePortParams = { PortNumber, 0 }; // in and out
DWORD dwBytes;
int res = DeviceIoControl(hHub, IOCTL_USB_HUB_CYCLE_PORT,
&CyclePortParams, sizeof(CyclePortParams),
&CyclePortParams, sizeof(CyclePortParams),
&dwBytes, NULL);
CloseHandle(hHub);
return ( (0 != res) && (0 == CyclePortParams.StatusReturned) );
}
//----------------------------------------------------------------------------------------
Points of Interest
Microsoft's documentation for IOCTL_USB_HUB_CYCLE_PORT is wrong: It says "Output buffer: None
" which is wrong. The StatusReturned struct
member leads to the conclusion that the USB_CYCLE_PORT_PARAMS struct
is intended as in and out buffer. It works that way and dwBytes
contains 8 afterwards, which is the expected sizeof(USB_CYCLE_PORT_PARAMS)
.
What Makes It Fail
In contrast to an attempt to restart a device by means of the Device Manager IOCTL_USB_HUB_CYCLE_PORT
does not ask for permission, so no driver can veto it and so it always succeeds.
But the port must have a USB device attached and a driver installed. Otherwise, it fails with error 433 (ERROR_NO_SUCH_DEVICE
) or since Windows 10 21H2 with error 50 (ERROR_NOT_SUPPORTED
) which again is misleading.
Windows 11 comes with a proper error 1167 (ERROR_DEVICE_NOT_CONNECTED
).
History
- 18th June, 2023 - First release
- 23rd March, 2024 - Replaced the
SetupDi
enumeration of all hubs by a simpleCM_Get_Device_Interface_List
call, which also removes the dependency on setupapi.dll