python-ivi / python-usbtmc

Provides a USBTMC driver for controlling instruments over USB
MIT License
160 stars 69 forks source link

[Windows] usb.core.USBError: [Errno None] libusb0-dll:err [claim_interface] could not claim interface 1, invalid configuration 0 #46

Open JonBensi opened 6 years ago

JonBensi commented 6 years ago

Hello,

I was working on a test for a device (LadyBug LB5918A Power Sensor) and decided to use python source code that a peer had already written to communicate with it. The code was only tested in a linux environment and being the masochist I am I decided to see if I could at least just identify the device on windows. After using pip to install pyusb & usbtmc, creating an inf filter with libusb-win32, I ran a brief test script to see if I could just query the device ("*IDN?"). Here's the section of the init from the API were the code fails.

try:
    self.PID = PID
    self.VID = VID
    # Initialize USBTMC interface with the LadyBug.
    self.instr =  usbtmc.Instrument(self.VID, self.PID)
    #self.instr =  usbtmc.Instrument(0x1A0D, 0x15D8)
    self.connected = True
    self.log.debug('Connecting to device with VID: '+str(self.VID)+' and PID: '+str(self.PID))
    self._send('SYST:PRES DEF')
    self.log.debug(str(self._request('*IDN?')))
    #self.clearErr()
except Exception as e: 
    self.exceptionHandler(e)

And the __send() function

def _send(self,msg):
        self.log.debug(str('Writing: '+str(msg)))
        self.instr.write(msg+'\n')
        err = str(self.instr.ask('SYST:ERR?\n'))
        if err != '+0,"No error"':
            self.log.debug(str('LadyBug Error: '+str(err)))

The error message I get in return is as follows

2018-08-03 15:29:08,970 :: ladyBug(DEBUG) :: __init__
2018-08-03 15:29:08,996 :: ladyBug(DEBUG) :: Connecting to device with VID: 6669 and PID: 5592
2018-08-03 15:29:08,997 :: ladyBug(DEBUG) :: Writing: SYST:PRES DEF
Traceback (most recent call last):
  File "ladyBug-test.py", line 10, in <module>
    lb = ladyBug.meter(0x1a0d,0x15d8,True)
  File "C:\Users\jonathan.bensing\LadyBug-API\ladyBug.py", line 49, in __init__
    self.exceptionHandler(e)
  File "C:\Users\jonathan.bensing\LadyBug-API\ladyBug.py", line 249, in exceptionHandler
    self.instr.open()
  File "C:\Python27\lib\site-packages\usbtmc\usbtmc.py", line 313, in open
    usb.util.claim_interface(self.device, self.iface)
  File "C:\Python27\lib\site-packages\usb\util.py", line 205, in claim_interface
    device._ctx.managed_claim_interface(device, interface)
  File "C:\Python27\lib\site-packages\usb\core.py", line 102, in wrapper
    return f(self, *args, **kwargs)
  File "C:\Python27\lib\site-packages\usb\core.py", line 167, in managed_claim_interface
    self.backend.claim_interface(self.handle, i)
  File "C:\Python27\lib\site-packages\usb\backend\libusb0.py", line 521, in claim_interface
    _check(_lib.usb_claim_interface(dev_handle, intf))
  File "C:\Python27\lib\site-packages\usb\backend\libusb0.py", line 431, in _check
    raise USBError(errmsg, ret)
usb.core.USBError: [Errno None] libusb0-dll:err [claim_interface] could not claim interface 1, invalid configuration 0

After a lot of googling, I couldn’t seem to find any conclusive reason why this shouldn’t work on windows seeing that I had followed all the steps correctly for both PyUSB and USBTMC setup. I tried just the PyUSB module to identify the device which I was able to do and I then tried to write to the device commands which I know I was most likely doing wrong. I did notice that for most of the examples of PyUSB, the set_configuration() function was used a lot to attach to a device. One post that had a similar error said that set_configuration(1) should be used and in a rather fluke way I was able to get the device working, so below is the script that cause the device to function properly.

myVendorId =0x1a0d
myProductId=0x15d8
import usb.core
dev = usb.core.find(idVendor=myVendorId, idProduct=myProductId)
print dev
dev.set_configuration(1)
import ladyBug
lb = ladyBug.meter(0x1a0d,0x15d8,True)
exit()

So I then decided to go through the USBTMC api to see where the issue may be occurring. I believe that the culprit is here in the open() function:

    def open(self):
        if self.connected:
            return
        # initialize device
        # find first USBTMC interface
        for cfg in self.device:
            for iface in cfg:
                if (self.device.idVendor == 0x1334) or \
                   (iface.bInterfaceClass == USBTMC_bInterfaceClass and
                    iface.bInterfaceSubClass == USBTMC_bInterfaceSubClass):
                    self.cfg = cfg
                    self.iface = iface
                    break
                else:
                    continue
            break
        if self.iface is None:
            raise UsbtmcException("Not a USBTMC device", 'init')
        try:
            self.old_cfg = self.device.get_active_configuration()
        except usb.core.USBError:
            # ignore exception if configuration is not set
            pass
        if self.old_cfg is not None and self.old_cfg.bConfigurationValue == self.cfg.bConfigurationValue:
            # already set to correct configuration
            # release kernel driver on USBTMC interface
            self._release_kernel_driver(self.iface.bInterfaceNumber)
        else:
            # wrong configuration or configuration not set
            # release all kernel drivers
            if self.old_cfg is not None:
                for iface in self.old_cfg:
                    self._release_kernel_driver(iface.bInterfaceNumber)
            # set proper configuration
            self.device.set_configuration(self.cfg)

        # claim interface
        usb.util.claim_interface(self.device, self.iface)

After stepping through the code manually, the issue is where self.device.set_configuration(self.cfg) is never being called. I’m not sure the reasoning behind having the configuration not being set, because it seems that (on windows at least) the old_cfg bConfigurationValue will always be the same as the active cfg bConfigurationValue. And within the usbtmc code I simple moved self.device.set_configuration(self.cfg) out of the if statement so that the device will always be set.

        if self.old_cfg is not None and self.old_cfg.bConfigurationValue == self.cfg.bConfigurationValue:
            # already set to correct configuration

            # release kernel driver on USBTMC interface
            self._release_kernel_driver(self.iface.bInterfaceNumber)
        else:
            # wrong configuration or configuration not set

            # release all kernel drivers
            if self.old_cfg is not None:
                for iface in self.old_cfg:
                    self._release_kernel_driver(iface.bInterfaceNumber)

        # set proper configuration
        self.device.set_configuration(self.cfg)

I’m not sure if this is intended but I believe that this is how it should be in the code. I hope this helps!

Jon

RobertGawron commented 5 years ago

I had the same problem and this fix solved it.