Get string descriptor using PyUSB usb.util.get_string()
Asked Answered
F

5

7

I am having trouble getting the string descriptor of a USB device. What I'm looking for is the human friendly Manufacturer and Product names. I am using libusb-1.0 as backend and am able to get the Manufacturer name using the provided libusb test program, so I know it exists.

PyUSB help file says you can access usb_get_string_simple (from libusb backend) using:

get_string(dev, length, index, langid=None)

   Retrieve a string descriptor from the device.
   dev is the Device object to which the request will be sent to.

   length is the length of string in number of characters.

   index is the string descriptor index and langid is the Language
   ID of the descriptor. If langid is omitted, the string descriptor
   of the first Language ID will be returned.

   The return value is the unicode string present in the descriptor.
import usb
#help(usb.core) 
busses = usb.busses()
for bus in busses:
  devices = bus.devices
  for dev in devices:
    _name = usb.util.get_string(dev.dev,256,0)  #This is where I'm having trouble
    print "device name=",_name
    print "Device:", dev.filename
    print "  Device class:",dev.deviceClass
    print "  Device sub class:",dev.deviceSubClass
    print "  Device protocol:",dev.deviceProtocol
    print "  Max packet size:",dev.maxPacketSize
    print "  idVendor:",hex(dev.idVendor)
    print "  idProduct:",hex(dev.idProduct)
    print "  Device Version:",dev.deviceVersion
    for config in dev.configurations:
      print "  Configuration:", config.value
      print "    Total length:", config.totalLength 
      print "    selfPowered:", config.selfPowered
      print "    remoteWakeup:", config.remoteWakeup
      print "    maxPower:", config.maxPower
      for intf in config.interfaces:
        print "    Interface:",intf[0].interfaceNumber
        for alt in intf:
          print "    Alternate Setting:",alt.alternateSetting
          print "      Interface class:",alt.interfaceClass
          print "      Interface sub class:",alt.interfaceSubClass
          print "      Interface protocol:",alt.interfaceProtocol
          for ep in alt.endpoints:
            print "      Endpoint:",hex(ep.address)
            print "        Type:",ep.type
            print "        Max packet size:",ep.maxPacketSize
            print "        Interval:",ep.interval

Any help will be appreciated.

Fellowman answered 10/5, 2011 at 0:19 Comment(2)
Can you post example output and clarify how it differs from what you'd like?Photoneutron
Output comes back as: "device name= Љ" When I use the libusb test application it returns "LG Electronics, Inc"Fellowman
L
7

It's not a good idea to hard-code the index.

I would recommend using something like this:

usb.util.get_string(dev, 256, dev.iSerialNumber)
usb.util.get_string(dev, 256, dev.iManufacturer)

As you will see below the index is different from device to device.

Output of lsusb -v:

device 1:            #index #string_descriptor
      iManufacturer  3 Linux 3.8.13 musb-hcd
      iProduct       2 MUSB HDRC host driver
      iSerial        1 musb-hdrc.1.auto

device 2:
      iManufacturer  2 E-boda
      iProduct       3 SUNNY V35
      iSerial        4 0123456789ABCDEF
Legation answered 29/8, 2014 at 20:40 Comment(1)
now the function parameters are a bit different, therefore it does not work with the 256 anymore. It should be: usb.uti.get_string(dev, dev.iSerialNUmber)Gangrene
C
6

(Update July 2019: See Teodor-Bogdan Barbieru's answer below for a clear description of why you should not actually hardcode indexes!)

(Second update July 2019: See gog's comment below for a link to the USB specification containing a table listing all the device descriptor fields.)

The following line in your code:

usb.util.get_string(dev.dev,256,0)

is indeed the problem. I'm not terribly sure what the USB specification says, but in my current hardware project, I get the following for choices of index:

  • index=0 (your choice) returns a single unicode character
  • index=1 sends the manufacturer
  • index=2 sends the device description
  • index=3 sends device serial number

So, try:

usb.util.get_string(dev.dev, 256, 2)

Good luck!

Carbonic answered 12/6, 2012 at 11:14 Comment(4)
NO! Don't hardcode any index. If you are in a hardware project, you should know that you can choose whatever index you want for your String descriptor. The right answer is to use dev.iProduct as the index. Moreover, index 0 will NOT return a single unicode character, but will return the list of supported LANGIDs from the USB device. Please edit your answer, as you are reporting wrong informationYablon
Sure, I'll edit my answer. Seven years ago, I was just describing what I got back from the device I had connected. The PyUSB documentation was not very clear on that point. If you can point me to a better description, I'll add that link to my answer. In the meantime, I'll point to Teodor-Bogdan Barbieru's answer below.Carbonic
The PyUSB documentation may not be clear because the USB spec already says it all ;). You can find it here. Specifically, in the usb_20.pdf file, at Chapter 9.6.1 there's a table that describes all the DEVICE descriptor fields. Chapter 9.6.7 describes STRING descriptors, and the different behavior of STRING descriptor at index 0Yablon
I've updated the answer with a pointer to your comment. Thanks, @gog!Carbonic
G
1

modify to usb.util.get_string(dev, dev.iSerialNumber)

Gatian answered 9/5, 2017 at 6:48 Comment(0)
F
0

This will retrieve the strings from the device:

dev = usb.core.find(idVendor=0x077d, idProduct=0x0410) # fill in your own device, of course
if dev is None:
    print 'Our device is not connected'
else:
    if dev._manufacturer is None:
        dev._manufacturer = usb.util.get_string(dev, dev.iManufacturer)
    print str(dev._manufacturer)
    if dev._product is None:
        dev._product = usb.util.get_string(dev, dev.iProduct)
    print str(dev._product)
Footstall answered 4/11, 2014 at 18:59 Comment(1)
This results with an TypeError: get_string() takes at least 3 arguments (2 given) because parameter length is not optional.Surmise
G
0

On Windows, usb.util_get_string(dev, index, lang) returns an error

'array.array' object has no attribute 'tostring'

Digging down, in ...lib\site-packages\usb\util.py, line 260, the code says

return buf[2:].tostring().decode('utf-16-le')

This may work on Linux, but on windows, buf is an array of integers. It needs to be changed to either a bytearray or a bytes object. There is no need to convert this to a string - bytes objects can be decoded to utf16

return bytes(buf[2:]).decode('utf-16-le')

Then everything magically starts working.

Also check the progress of bytearray addition to array.array. If this is implemented, this hack may not be required.

Groce answered 27/8, 2023 at 8:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.