1

My python code as follow

#!/usr/bin/env python

from __future__ import print_function
import argparse
import string
import struct
import sys

import win32api
import win32file
import pywintypes


def CTL_CODE(DeviceType, Function, Method, Access):
    return (DeviceType << 16) | (Access << 14) | (Function << 2) | Method
def USB_CTL(id):
   # CTL_CODE(FILE_DEVICE_USB, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
    return CTL_CODE(0x22, id, 0, 0)

IOCTL_USB_GET_ROOT_HUB_NAME = USB_CTL(258)                   # HCD_GET_ROOT_HUB_NAME
IOCTL_USB_GET_NODE_INFORMATION = USB_CTL(258)                # USB_GET_NODE_INFORMATION
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION = USB_CTL(259)     # USB_GET_NODE_CONNECTION_INFORMATION
IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME = USB_CTL(264)  # USB_GET_NODE_CONNECTION_DRIVERKEY_NAME
IOCTL_USB_GET_NODE_CONNECTION_NAME = USB_CTL(261)            # USB_GET_NODE_CONNECTION_NAME
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION = USB_CTL(260) # USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION

USB_CONFIGURATION_DESCRIPTOR_TYPE = 2
USB_STRING_DESCRIPTOR_TYPE = 3
USB_INTERFACE_DESCRIPTOR_TYPE = 4
MAXIMUM_USB_STRING_LENGTH = 255


def open_dev(name):
    try:
        handle = win32file.CreateFile(name,
                                  win32file.GENERIC_WRITE,
                                  win32file.FILE_SHARE_WRITE,
                                  None,
                                  win32file.OPEN_EXISTING,
                                  0,
                                  None)
    except pywintypes.error as e:
        return None
    return handle


def get_root_hub_name(handle):
    buf = win32file.DeviceIoControl(handle,
                                IOCTL_USB_GET_ROOT_HUB_NAME,
                                None,
                                6,
                                None)
    act_len, _ = struct.unpack('LH', buf)
    buf = win32file.DeviceIoControl(handle,
                                IOCTL_USB_GET_ROOT_HUB_NAME,
                                None,
                                act_len,
                                None)
    return buf[4:].decode('utf-16le')


def get_driverkey_name(handle, index):
    key_name = chr(index) + '\0'*9
    try:
        buf = win32file.DeviceIoControl(handle,
                                    IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
                                    key_name,
                                    10,
                                    None)
    except pywintypes.error as e:
        print(e.strerror, index)
        sys.exit(1)
    _, act_len, _ = struct.unpack('LLH', buf)
    buf = win32file.DeviceIoControl(handle,
                                IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
                                key_name,
                                act_len,
                                None)
    return buf[8:].decode('utf-16le')


def get_ext_hub_name(handle, index):
    hub_name = chr(index) + '\0'*9
    buf = win32file.DeviceIoControl(handle,
                                IOCTL_USB_GET_NODE_CONNECTION_NAME,
                                hub_name,
                                10,
                                None)
    _, act_len, _ = struct.unpack('LLH', buf)
    buf = win32file.DeviceIoControl(handle,
                                IOCTL_USB_GET_NODE_CONNECTION_NAME,
                                hub_name,
                                act_len,
                                None)
    return buf[8:].decode('utf-16le')


def get_str_desc(handle, conn_idx, str_idx):
    req = struct.pack('LBBHHH',
                  conn_idx,
                  0,
                  0,
                  (USB_STRING_DESCRIPTOR_TYPE<<8) | str_idx,
                  win32api.GetSystemDefaultLangID(),
                  12+MAXIMUM_USB_STRING_LENGTH)
    try:
        buf = win32file.DeviceIoControl(handle,
                                    IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
                                    req,
                                    12+MAXIMUM_USB_STRING_LENGTH,
                                    None)
    except pywintypes.error as e:
         return 'ERROR: no String Descriptor for index {}'.format(str_idx)
    if len(buf) > 16:
        return buf[14:].decode('utf-16le')
    return ''


def exam_hub(name, verbose, level):
    handle = open_dev(r'\\.\{}'.format(name))
    if not handle:
        print('Failed to open device {}'.format(name))
        return
    buf = win32file.DeviceIoControl(handle,
                                IOCTL_USB_GET_NODE_INFORMATION,
                                None,
                                76,
                                None)
    print_hub_ports(handle, ord(buf[6]), verbose, level)
    handle.close()


def print_str_or_hex(to_be_print):
    if all(c in string.printable for c in to_be_print):
        print('"{}"'.format(to_be_print))
        return
    print('Hex: ', end='')
    for x in to_be_print:
        print('{:02x} '.format(ord(x)), end='')
    print('')


def print_hub_ports(handle, num_ports, verbose, level):
    for idx in range(1, num_ports+1):
        info = chr(idx) + '\0'*34
        try:
            buf = win32file.DeviceIoControl(handle,
                                        IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
                                        info,
                                        34 + 11*30,
                                        None)
        except pywintypes.error as e:
            print(e.winerror, e.funcname, e.strerror)
            return

       _, vid, pid, vers, manu, prod, seri, _, ishub, _, stat = struct.unpack('=12sHHHBBB3s?6sL', buf[:35])

        if ishub:
            if verbose:
                print('{}  [Port{}] {}'.format('  '*level, idx, 'USB Hub'))
            exam_hub(get_ext_hub_name(handle, idx), verbose, level)
       elif stat == 0 and verbose:
            print('{}  [Port{}] {}'.format('  '*level, idx, 'NoDeviceConnected'))
        elif stat == 1:
            if verbose or (manu != 0 or prod != 0 or seri != 0):
                print('{}  [Port{}] {}'.format('  '*level, idx, get_driverkey_name(handle, idx)))
                print('{}    Vendor ID:    0x{:04X}'.format('  '*level, vid))
                print('{}    Product ID:  0x{:04X}'.format('  '*level, pid))
                print('{}    Device BCD:  0x{:04X}'.format('  '*level, vers))
                if manu != 0:
                    print('{}    Manufacturer (0x{:x}) -> '.format('  '*level, manu), end='')
                    print_str_or_hex(get_str_desc(handle, idx, manu))
                if prod != 0:
                    print('{}    Product      (0x{:x}) -> '.format('  '*level, prod), end='')
                    print_str_or_hex(get_str_desc(handle, idx, prod))
                if seri != 0:
                    print('{}    Serial No    (0x{:x}) -> '.format('  '*level, seri), end='') 
                    print_str_or_hex(get_str_desc(handle, idx, seri))


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', '--verbose', action='store_true',
                    help="Increase output verbosity.")
    args = parser.parse_args()

    for i in range(10):
        name = r"\\.\HCD{}".format(i)
        handle = open_dev(name)
        if not handle:
            continue

        root = get_root_hub_name(handle)
        print('{}RootHub: {}'.format('\n' if i != 0 else '', root))

        dev_name = r'\\.\{}'.format(root)
        dev_handle = open_dev(dev_name)
        if not dev_handle:
            print('Failed to open device {}'.format(dev_name))
            continue

        buf = win32file.DeviceIoControl(dev_handle,
                                    IOCTL_USB_GET_NODE_INFORMATION,
                                    None,
                                    76,
                                    None)
        print_hub_ports(dev_handle, ord(buf[6]), args.verbose, 0)
        dev_handle.close()
        handle.close()

if __name__ == '__main__':
    main()

and the return is:

RootHub: USB#ROOT_HUB20#4&2c86c80d&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}

[Port2] {36fc9e60-c465-11cf-8056-444553540000}\0009

Vendor ID: 0x2232

Product ID: 0x6001

Device BCD: 0x0002

Manufacturer (0x1) -> "ERROR: no String Descriptor for index 1"

Product (0x2) -> "ERROR: no String Descriptor for index 2"

The question is : How can I get the Manufacturer and Product number?

user3579757
  • 53
  • 2
  • 5

2 Answers2

0

It is possible that this device doesn't provide any string descriptor.

Although the index of the string descriptors (iManufacturer, iProduct, iSerialNumber) should be zero to show that there is no string, many devices are seen reporting incorrect (non-zero) index like what you saw. I believe there is not much you can do except to ask for vendor's help to upgrade its firmware.

However, Vendor ID and Product ID are providing the information you need. Vendor ID 0x2232 would be Silicon Motion and Product ID 0x6001 i am not sure what it is, maybe you need to check with vendor on this as well.

Winfred
  • 111
  • 2
  • Dear Winfred: Device Descriptor: bcdUSB: 0x0200 bDeviceClass: 0xEF bDeviceSubClass: 0x02 bDeviceProtocol: 0x01 bMaxPacketSize0: 0x40 (64) idVendor: 0x2232 idProduct: 0x6001 bcdDevice: 0x0002 iManufacturer: 0x01 0x0409: "XXXXXX00010200144W11111" iProduct: 0x02 0x0409: "XXX-XX02" iSerialNumber: 0x00 bNumConfigurations: 0x01 I can use USBView to get iManufacturer & iProduct numbers of Microsoft, but I cannot extract from the program, because it is too huge. Do you have any suggest? – user3579757 Jul 16 '14 at 02:42
  • OK. What did you mean by "cannot extract from the program"? The maximum length of string representing iManufacturer and iProduct would up to 255, and the script should be able to handle it. Would you print the buffer length returning by get_str_desc()? And also please try to hex dump the buffer instead of just print the strings. – Winfred Jul 16 '14 at 13:06
0

Your code is very close to doing exactly what you want (and also what I needed). The issue is with the buffer length passed in the packed iocontrolcode structure. It needs to be MAXIMUM_USB_STRING_LENGTH instead of 12+MAXIMUM_USB_STRING_LENGTH, shown below.
Note that I left a commented out print(e) statement in the code. This pointed me to the problem as the resulting error message complained about an incorrect buffer size.

def get_str_desc(handle, conn_idx, str_idx):
    req = struct.pack('LBBHHH',
                  conn_idx,
                  0,
                  0,
                  (USB_STRING_DESCRIPTOR_TYPE<<8) | str_idx,
                  win32api.GetSystemDefaultLangID(),
                  MAXIMUM_USB_STRING_LENGTH)
    try:
        buf = win32file.DeviceIoControl(handle,
                                    IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
                                    req,
                                    12+MAXIMUM_USB_STRING_LENGTH,
                                    None)
    except pywintypes.error as e:
        #print(e)
        return 'ERROR: no String Descriptor for index {}'.format(str_idx)
    if len(buf) > 16:
        return buf[14:].decode('utf-16le')
    return ''

With this change the Manufacturer, Product, and Serial strings will now be displayed (if available).

Timothy
  • 2,004
  • 3
  • 23
  • 29
urbite
  • 1
  • 1