Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

How to Restart a USB Port

5.00/5 (20 votes)
23 Mar 2024CPOL2 min read 36.3K   945  
Shows how to use IOCTL_USB_HUB_CYCLE_PORT to restart a USB port under Windows
Shows how to find a USB device in the Windows Device Management by a given Device Instance ID, determine the used USB port number, get its parent device (its USB hub), open the hub and perform the IOCTL_USB_HUB_CYCLE_PORT call.

  

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.

C++
//--------------------------------------------------------------------------------
// 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 simple CM_Get_Device_Interface_List call, which also removes the dependency on setupapi.dll

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)