Bad Handle to IOCTL_USB_GET_HUB_INFORMATION_EX?

Hello,

I tried to fold this into my post about port resets but realized I
still need to come back to those.

The code below works in one case but not in another. I have two
functions: EnumHubs and EnumUSBDevices that both need to call the same
IOCTL. The code which follows is from EnumUSBDevices, which is passed
a wstring explicitly. EnumHubs uses a stack to visit all hubs. The
class of the devices in both cases should be GUID_DEVINTERFACE_USB_HUB
which is what the IOCTL requires.

The problem persists if I skip calling my EnumHubs function and
instead pass a list of device endpoints gathered with SetupAPI (note
the output of the two methods matches, indicating I’m doing this
properly). I’m not sure what else to check here. The code is very
close to the driver examples code.

HANDLE hHub = CreateFile(
hub.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (!hHub) {
wprintf(L"EnumUSBDevices: CreateFile: %d\n", GetLastError());
exit(1);
}

USB_HUB_INFORMATION_EX uhie; DWORD nBytes = 0;
memset(&uhie, 0, sizeof(USB_HUB_INFORMATION_EX));
if (!DeviceIoControl(
hHub,
IOCTL_USB_GET_HUB_INFORMATION_EX,
&uhie, sizeof(USB_HUB_INFORMATION_EX),
&uhie, sizeof(USB_HUB_INFORMATION_EX),
&nBytes,
NULL
)) {
wprintf(L"EnumUSBDevices: DeviceIoControl 1: %d, %s\n",
GetLastError(), hub.c_str());

I originally had the input fields in the DeviceIoControl call empty
but the kernel accepted them. I filled them in per the USBView
example.

Thanks in advance,
R0b0t1

On Jun 16, 2018, at 9:55 PM, xxxxx@gmail.com wrote:
>
> The code below works in one case but not in another. I have two
> functions: EnumHubs and EnumUSBDevices that both need to call the same
> IOCTL. The code which follows is from EnumUSBDevices, which is passed
> a wstring explicitly. EnumHubs uses a stack to visit all hubs. The
> class of the devices in both cases should be GUID_DEVINTERFACE_USB_HUB
> which is what the IOCTL requires.

What doesn’t work? What error do you get, and where?

Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

On Sun, Jun 17, 2018 at 1:59 AM, xxxxx@probo.com wrote:
> On Jun 16, 2018, at 9:55 PM, xxxxx@gmail.com wrote:
>>
>> The code below works in one case but not in another. I have two
>> functions: EnumHubs and EnumUSBDevices that both need to call the same
>> IOCTL. The code which follows is from EnumUSBDevices, which is passed
>> a wstring explicitly. EnumHubs uses a stack to visit all hubs. The
>> class of the devices in both cases should be GUID_DEVINTERFACE_USB_HUB
>> which is what the IOCTL requires.
>
> What doesn’t work? What error do you get, and where?

Er, my apologies. I thought I put it in the title. I get 0x6,
ERROR_INVALID_HANDLE. This only happens when I try to enumerate a
single hub specifically. When I iterate through all hubs and devices
to only find the hubs it seems to work, and the code is as far as I
can tell exactly the same. The error is in EnumUSBDevices at the first
IOCTL.

EnumHubs:

vector EnumHubs(
In const vector rhubs
)
{
vector r;
stack> s(rhubs);

while (!s.empty()) {
const auto hub = s.top(); s.pop();
HANDLE hHub = CreateFile(
hub.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (!hHub) {
wprintf(L"CreateFile: %d\n", GetLastError());
exit(1);
}

USB_HUB_INFORMATION_EX uhie; DWORD nBytes = 0;
memset(&uhie, 0, sizeof(USB_HUB_INFORMATION_EX));
if (!DeviceIoControl(
hHub,
IOCTL_USB_GET_HUB_INFORMATION_EX,
NULL, NULL,
&uhie, sizeof(USB_HUB_INFORMATION_EX),
&nBytes,
NULL
)) {
wprintf(L"DeviceIoControl: %d\n", GetLastError());
}
wprintf(L"%u %d\n", nBytes, sizeof(USB_HUB_INFORMATION_EX));

for (int i = 1; i <= uhie.HighestPortNumber; i++) {
USB_NODE_CONNECTION_INFORMATION_EX uncie;
memset(&uncie, 0, sizeof(USB_NODE_CONNECTION_INFORMATION_EX));
uncie.ConnectionIndex = i;
if (!DeviceIoControl(
hHub,
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
&uncie, sizeof(USB_NODE_CONNECTION_INFORMATION_EX),
&uncie, sizeof(USB_NODE_CONNECTION_INFORMATION_EX),
NULL,
NULL
)) {
wprintf(L"DeviceIoControl: %d\n", GetLastError());
}

if (!uncie.DeviceIsHub)
continue;

USB_NODE_CONNECTION_NAME uncn;
memset(&uncn, 0, sizeof(USB_NODE_CONNECTION_NAME));
uncn.ConnectionIndex = i;
if (!DeviceIoControl(
hHub,
IOCTL_USB_GET_NODE_CONNECTION_NAME,
&uncn, sizeof(USB_NODE_CONNECTION_NAME),
&uncn, sizeof(USB_NODE_CONNECTION_NAME),
NULL,
NULL
)) {
wprintf(L"DeviceIoControl: %d\n", GetLastError());
}

PUSB_NODE_CONNECTION_NAME puncn =
(PUSB_NODE_CONNECTION_NAME)malloc(uncn.ActualLength);
puncn->ConnectionIndex = i;
if (!DeviceIoControl(
hHub,
IOCTL_USB_GET_NODE_CONNECTION_NAME,
puncn, sizeof(USB_NODE_CONNECTION_NAME),
puncn, uncn.ActualLength,
NULL,
NULL
)) {
wprintf(L"DeviceIoControl: %d\n", GetLastError());
}

auto t = L"\\?\" + wstring(puncn->NodeName);
r.push_back(t);
s.push(t);
}
}

return r;
}

And EnumUSBDevices:

vector EnumUSBDevices(
In const wstring hub
)
{
wprintf(L"%s\n", hub.c_str());
vector r;

HANDLE hHub = CreateFile(
hub.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (!hHub) {
wprintf(L"EnumUSBDevices: CreateFile: %d\n", GetLastError());
exit(1);
}

USB_HUB_INFORMATION_EX uhie; DWORD nBytes = 0;
memset(&uhie, 0, sizeof(USB_HUB_INFORMATION_EX));
if (!DeviceIoControl(
hHub,
IOCTL_USB_GET_HUB_INFORMATION_EX,
&uhie, sizeof(USB_HUB_INFORMATION_EX),
&uhie, sizeof(USB_HUB_INFORMATION_EX),
&nBytes,
NULL
)) {
wprintf(L"EnumUSBDevices: DeviceIoControl 1: %d, %s\n",
GetLastError(), hub.c_str());
}
wprintf(L"%u %d\n", nBytes, sizeof(USB_HUB_INFORMATION_EX));

for (int i = 1; i <= uhie.HighestPortNumber; i++) {
USB_NODE_CONNECTION_INFORMATION_EX uncie;
memset(&uncie, 0, sizeof(USB_NODE_CONNECTION_INFORMATION_EX));
uncie.ConnectionIndex = i;
if (!DeviceIoControl(
hHub,
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
&uncie, sizeof(USB_NODE_CONNECTION_INFORMATION_EX),
&uncie, sizeof(USB_NODE_CONNECTION_INFORMATION_EX),
NULL,
NULL
)) {
wprintf(L"EnumUSBDevices: DeviceIoControl 2: %d\n", GetLastError());
}

USB_NODE_CONNECTION_NAME uncn;
memset(&uncn, 0, sizeof(USB_NODE_CONNECTION_NAME));
uncn.ConnectionIndex = i;
if (!DeviceIoControl(
hHub,
IOCTL_USB_GET_NODE_CONNECTION_NAME,
&uncn, sizeof(USB_NODE_CONNECTION_NAME),
&uncn, sizeof(USB_NODE_CONNECTION_NAME),
NULL,
NULL
)) {
wprintf(L"EnumUSBDevices: DeviceIoControl 3: %d\n", GetLastError());
}

PUSB_NODE_CONNECTION_NAME puncn =
(PUSB_NODE_CONNECTION_NAME)malloc(uncn.ActualLength);
puncn->ConnectionIndex = i;
if (!DeviceIoControl(
hHub,
IOCTL_USB_GET_NODE_CONNECTION_NAME,
puncn, sizeof(USB_NODE_CONNECTION_NAME),
puncn, uncn.ActualLength,
NULL,
NULL
)) {
wprintf(L"EnumUSBDevices: DeviceIoControl 4: %d\n", GetLastError());
}

auto t = L"\\?\" + wstring(puncn->NodeName);
r.push_back(t);
}

return r;
}

Thanks in advance,
R0b0t1

On Jun 17, 2018, at 12:05 AM, xxxxx@gmail.com wrote:
>
> Er, my apologies. I thought I put it in the title. I get 0x6,
> ERROR_INVALID_HANDLE. This only happens when I try to enumerate a
> single hub specifically. When I iterate through all hubs and devices
> to only find the hubs it seems to work, and the code is as far as I
> can tell exactly the same. The error is in EnumUSBDevices at the first
> IOCTL.

Remember that root hubs (the default hub at the lowest level, just above the host controller) do not implement IOCTL_HUB_GET_INFORMATION_EX. You’re printing out the hub names. What hub names are failing?

Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

On Sun, Jun 17, 2018 at 2:14 AM, xxxxx@probo.com wrote:
> On Jun 17, 2018, at 12:05 AM, xxxxx@gmail.com wrote:
>>
>> Er, my apologies. I thought I put it in the title. I get 0x6,
>> ERROR_INVALID_HANDLE. This only happens when I try to enumerate a
>> single hub specifically. When I iterate through all hubs and devices
>> to only find the hubs it seems to work, and the code is as far as I
>> can tell exactly the same. The error is in EnumUSBDevices at the first
>> IOCTL.
>
> Remember that root hubs (the default hub at the lowest level, just above the host controller) do not implement IOCTL_HUB_GET_INFORMATION_EX. You’re printing out the hub names. What hub names are failing?

All hubs on my system. I plug in a USB 3 hub which is implemented with
two hub controllers so two hubs appear. The root hub should be a valid
target for the IOCTL, see the helper function below which returns the
root hub as an instance of GUID_DEVINTERFACE_USB_HUB. The EnumHubs
function works by taking a vector of all root hub pathnames and
requesting all connected devices just like for a hub. It puts the
passed root hubs into a stack. When it encounters a hub it pushes it
onto the stack. It iterates over each element in the stack. The
EnumHubs function does not generate any messages.

So, the output is:

hub: \?\usb#vid_2109&pid_0813#5&2c705bfe&0&13#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
\?\usb#vid_2109&pid_0813#5&2c705bfe&0&13#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
EnumUSBDevices: DeviceIoControl 1: 6,
\?\usb#vid_2109&pid_0813#5&2c705bfe&0&13#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
3435973836 77
hub: \?\usb#vid_2109&pid_2813#5&2c705bfe&0&1#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
\?\usb#vid_2109&pid_2813#5&2c705bfe&0&1#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
EnumUSBDevices: DeviceIoControl 1: 6,
\?\usb#vid_2109&pid_2813#5&2c705bfe&0&1#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
3435973836 77
hub: \?\usb#root_hub30#4&db2fc44&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
\?\usb#root_hub30#4&db2fc44&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
EnumUSBDevices: DeviceIoControl 1: 6,
\?\usb#root_hub30#4&db2fc44&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
3435973836 77

And the code:

int main()
{
for (const auto hub: EnumDevices(GUID_DEVINTERFACE_USB_HUB)) {
wprintf(L"hub: %s\n", hub.c_str());
for (const auto port: EnumUSBDevices(hub)) {
wprintf(L"port: %s\n", port.c_str());
}
}
}

vector EnumDevices(
In const GUID Guid
)
{
vector r;

int index = 0;
HDEVINFO hDevInfo = SetupDiGetClassDevs(&Guid, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

SP_DEVINFO_DATA DevInfoData;
memset(&DevInfoData, 0, sizeof(SP_DEVINFO_DATA));
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

while (SetupDiEnumDeviceInfo(hDevInfo, index, &DevInfoData)) {
index++;

int jndex = 0;
SP_DEVICE_INTERFACE_DATA DevIntData;
memset(&DevIntData, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
DevIntData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

while (SetupDiEnumDeviceInterfaces(
hDevInfo,
&DevInfoData, &Guid, jndex, &DevIntData
)) {
jndex++;

// Get the size required for the structure.
DWORD RequiredSize;
SetupDiGetDeviceInterfaceDetail(
hDevInfo, &DevIntData, NULL, NULL, &RequiredSize, NULL
);

PSP_DEVICE_INTERFACE_DETAIL_DATA pDevIntDetData =
(PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(
sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + RequiredSize
);
memset(pDevIntDetData, 0,
sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + RequiredSize);
pDevIntDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

SetupDiGetDeviceInterfaceDetail(
hDevInfo,
&DevIntData,
pDevIntDetData, RequiredSize,
NULL,
&DevInfoData
);

r.push_back(wstring(pDevIntDetData->DevicePath));
free(pDevIntDetData);
}
}

return r;
}

Thanks,
R0b0t1

On Sun, Jun 17, 2018 at 11:11 AM, R0b0t1 wrote:
> On Sun, Jun 17, 2018 at 2:14 AM, xxxxx@probo.com wrote:
>> On Jun 17, 2018, at 12:05 AM, xxxxx@gmail.com wrote:
>>>
>>> Er, my apologies. I thought I put it in the title. I get 0x6,
>>> ERROR_INVALID_HANDLE. This only happens when I try to enumerate a
>>> single hub specifically. When I iterate through all hubs and devices
>>> to only find the hubs it seems to work, and the code is as far as I
>>> can tell exactly the same. The error is in EnumUSBDevices at the first
>>> IOCTL.
>>
>> Remember that root hubs (the default hub at the lowest level, just above the host controller) do not implement IOCTL_HUB_GET_INFORMATION_EX. You’re printing out the hub names. What hub names are failing?
>
> All hubs on my system. I plug in a USB 3 hub which is implemented with
> two hub controllers so two hubs appear. The root hub should be a valid
> target for the IOCTL, see the helper function below which returns the
> root hub as an instance of GUID_DEVINTERFACE_USB_HUB. The EnumHubs
> function works by taking a vector of all root hub pathnames and
> requesting all connected devices just like for a hub. It puts the
> passed root hubs into a stack. When it encounters a hub it pushes it
> onto the stack. It iterates over each element in the stack. The
> EnumHubs function does not generate any messages.
>
> So, the output is:
>
> hub: \?\usb#vid_2109&pid_0813#5&2c705bfe&0&13#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
> \?\usb#vid_2109&pid_0813#5&2c705bfe&0&13#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
> EnumUSBDevices: DeviceIoControl 1: 6,
> \?\usb#vid_2109&pid_0813#5&2c705bfe&0&13#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
> 3435973836 77
> hub: \?\usb#vid_2109&pid_2813#5&2c705bfe&0&1#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
> \?\usb#vid_2109&pid_2813#5&2c705bfe&0&1#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
> EnumUSBDevices: DeviceIoControl 1: 6,
> \?\usb#vid_2109&pid_2813#5&2c705bfe&0&1#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
> 3435973836 77
> hub: \?\usb#root_hub30#4&db2fc44&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
> \?\usb#root_hub30#4&db2fc44&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
> EnumUSBDevices: DeviceIoControl 1: 6,
> \?\usb#root_hub30#4&db2fc44&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
> 3435973836 77
>
>
> And the code:
>
> int main()
> {
> for (const auto hub: EnumDevices(GUID_DEVINTERFACE_USB_HUB)) {
> wprintf(L"hub: %s\n", hub.c_str());
> for (const auto port: EnumUSBDevices(hub)) {
> wprintf(L"port: %s\n", port.c_str());
> }
> }
> }
>
> vector EnumDevices(
> In const GUID Guid
> )
> {
> vector r;
>
> int index = 0;
> HDEVINFO hDevInfo = SetupDiGetClassDevs(&Guid, NULL, NULL,
> DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
>
> SP_DEVINFO_DATA DevInfoData;
> memset(&DevInfoData, 0, sizeof(SP_DEVINFO_DATA));
> DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
>
> while (SetupDiEnumDeviceInfo(hDevInfo, index, &DevInfoData)) {
> index++;
>
> int jndex = 0;
> SP_DEVICE_INTERFACE_DATA DevIntData;
> memset(&DevIntData, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
> DevIntData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
>
> while (SetupDiEnumDeviceInterfaces(
> hDevInfo,
> &DevInfoData, &Guid, jndex, &DevIntData
> )) {
> jndex++;
>
> // Get the size required for the structure.
> DWORD RequiredSize;
> SetupDiGetDeviceInterfaceDetail(
> hDevInfo, &DevIntData, NULL, NULL, &RequiredSize, NULL
> );
>
> PSP_DEVICE_INTERFACE_DETAIL_DATA pDevIntDetData =
> (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(
> sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + RequiredSize
> );
> memset(pDevIntDetData, 0,
> sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + RequiredSize);
> pDevIntDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
>
> SetupDiGetDeviceInterfaceDetail(
> hDevInfo,
> &DevIntData,
> pDevIntDetData, RequiredSize,
> NULL,
> &DevInfoData
> );
>
> r.push_back(wstring(pDevIntDetData->DevicePath));
> free(pDevIntDetData);
> }
> }
>
> return r;
> }
>
> Thanks,
> R0b0t1

I think I found the problem. If a hub is opened opened a second time
without closing the first handle the IOCTL fails. Closing the handle
in EnumHubs solves the issue.

Cheers,
R0b0t1