Ошибка USB 9 во время передачи управления

Я пытаюсь сделать небольшой драйвер для связи с USB-термометром, используя java-библиотеку Libusb (org.usb4java.LibUsb). Я тестирую его на Raspeberry Pi (3b) с vanilla linux-arm.

Моя проблема в том, что мне не удается передать передачу управления на устройство. Я получаю сообщение об ошибке:

org.usb4java.LibUsbException: ошибка USB 9: ошибка передачи управления: ошибка канала

Вот мой код:

Основной класс:

public class usbDriver {

    public static void main(String[] args) {
        Communication2 com = new Communication2();
        try {
            com.trouverDevice();
            com.preparerCom();
            com.testCom();
            com.terminerCom();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Связь2 класс:

public class Communication2 {

/** vendor ID du thermometre */
private static final short VENDOR_ID = 0x1941;

/** product ID du thermometre  */
private static final short PRODUCT_ID = (short) 0x8021;

/** interface active du thermometre  */
private static final byte INTERFACE_ID = 0x0;

/** endpoint sur l'interface active du thermometre  */
private static final byte ENDPOINT_ID = (byte) 0x81;

private Context contexte = null;
private Device device = null;
DeviceHandle handle = null;

private boolean pret;
private boolean detach = false;
private boolean trouve = false;

public Communication2() {
    pret = false;
}

public void trouverDevice() throws SecurityException, UsbException{
    // avec libUsb
    // Create the libusb context
    Context context = new Context();

    // Initialize the libusb context
    int result = LibUsb.init(context);
    if (result < 0)
    {
        throw new LibUsbException("Unable to initialize libusb", result);
    }
    // Read the USB device list
    DeviceList list = new DeviceList();
    result = LibUsb.getDeviceList(context, list);
    if (result < 0)
    {
        throw new LibUsbException("Unable to get device list", result);
    }
    try
    {
        // Iterate over all devices and list them
        for (Device device: list)
        {
            int address = LibUsb.getDeviceAddress(device);
            int busNumber = LibUsb.getBusNumber(device);
            DeviceDescriptor descriptor = new DeviceDescriptor();
            result = LibUsb.getDeviceDescriptor(device, descriptor);
            if (result < 0)
            {
                throw new LibUsbException(
                    "Unable to read device descriptor", result);
            }
            if (descriptor.idVendor() == VENDOR_ID && descriptor.idProduct() == PRODUCT_ID){
                System.out.println("Thermometre Pearl NC-7004 detecté !");
                System.out.println(descriptor.toString());
                this.device = device;
                this.trouve=true;
            } 
        }
    }
    finally
    {
        // Ensure the allocated device list is freed
        //LibUsb.freeDeviceList(list, true);
    }
    // Deinitialize the libusb context  
}

    public boolean preparerCom() throws Exception{

        if (!this.trouve) return false;

        this.contexte = new Context();
        int result = LibUsb.init(contexte);

        // reclamer le handle
        System.out.println("claim device handle");
        this.handle = new DeviceHandle();
        result = LibUsb.open(this.device, handle);
        if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to open USB device", result);

        detach = LibUsb.hasCapability(LibUsb.CAP_SUPPORTS_DETACH_KERNEL_DRIVER);
        detach = true; // pour forcer le claim sur le kernel
        detach = detach && (LibUsb.kernelDriverActive(handle, INTERFACE_ID)==1?true:false);
        System.out.println(LibUsb.hasCapability(LibUsb.CAP_SUPPORTS_DETACH_KERNEL_DRIVER));
        System.out.println((LibUsb.kernelDriverActive(handle, INTERFACE_ID)));
        System.out.println(detach);

        // Detach the kernel driver
        if (detach)
        {
            System.out.println("tentative de detacher le kernel");
            result = LibUsb.detachKernelDriver(handle,  INTERFACE_ID);
            if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to detach kernel driver", result);
        }
        detach = true;  

        System.out.println("claim interface");
        result = LibUsb.claimInterface(handle, INTERFACE_ID);
        if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to claim interface", result);

        this.pret=false;
        return this.pret;
    }


    public void testCom(){
        if (!this.pret) return;

        ByteBuffer buffer = ByteBuffer.allocate(18);


        // LibUsb.fillControlSetup(buffer, (byte)0x80, (byte)0x6,
        //      (short)0x1, (short)0x0, (short)0x1200);

        ByteBuffer buffer2 = ByteBuffer.allocateDirect(18);

        int transfered = LibUsb.controlTransfer(handle,(byte)0x80,(byte)0x6,(short)0x1,(short)0x0,buffer2,2000L);
        if (transfered < 0) throw new LibUsbException("Control transfer failed", transfered);
        System.out.println(transfered + " bytes sent");

        String test;
        String test2;
        if (buffer2.hasArray()) {
            for(int i =0;i<buffer2.array().length;i++){
                System.out.format("%02x",buffer2.array()[i]);
            }


             test=  new String(buffer.array(),
                    buffer.arrayOffset() + buffer.position(),
                    buffer.remaining());
        } else {
            final byte[] b = new byte[buffer.remaining()];
            buffer.duplicate().get(b);
            test =  new String(b);
        }
        System.out.println(test);
    }


    public void terminerCom() throws Exception{
        if (this.pret){
            if (this.detach)
            {
                int result = LibUsb.attachKernelDriver(handle,  INTERFACE_ID);
                if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to re-attach kernel driver", result);
            }

            LibUsb.close(this.handle);
            this.trouve = false;
            this.pret = false;
        }
    }
}

Ошибка возникает, когда я звоню LibUsb.ControlTransfer(), пытаясь передать контрольный пакет GET_DESCRIPTOR. Вот полный возврат кода с информацией об дескрипторе устройства:

> Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.10
bDeviceClass 0 Per Interface
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x1941
idProduct 0x8021
bcdDevice 1.00
iManufacturer 0
iProduct 0
iSerial 0
bNumConfigurations 1
claim device handle
false
0
false
claim interface
org.usb4java.LibUsbException: USB error 9: Control transfer failed: Pipe error
at usbDriver.Communication2.testCom(Communication2.java:171)
at usbDriver.usbDriver.main(usbDriver.java:36)
root@raspberrypi:/home/pi/Desktop/execUsbDriver# java -jar usbDriver_executable.jar
java.lang.IllegalArgumentException: handle must not be null
at org.usb4java.LibUsb.controlTransfer(Native Method)
at usbDriver.Communication2.testCom(Communication2.java:170)
at usbDriver.usbDriver.main(usbDriver.java:36) 

Я думаю, что канал хорошо инициализирован (дескриптор кажется в порядке, интерфейс тоже успешно заявлен). Также документ Libusb указывает:

LIBUSB_ERROR_PIPE если запрос на управление не поддерживался устройством

Так что, я думаю, я просто неправильно выполняю запрос на управление. Если вы знаете код для правильной отправки запроса GET_DESCRIPTOR, я был бы рад его протестировать!


person Belze88    schedule 25.10.2016    source источник


Ответы (5)


На самом деле мне удалось выполнить первый запрос на моем устройстве! Это была просто проблема с отправленными запросами, которые не были точными.

Нет, я хочу справиться с основной задачей: получить данные с термометра. С помощью сниффинга я увидел, что должен отправить установочный пакет + 8 байт переменной через канал управления. Затем в конечной точке 0x81 устройство должно отправить обратно 4 * 8 байт данных в режиме прерывания. Я проверил это с помощью инструмента прототипирования:

экран запроса данных

Насколько я понимаю, я должен сделать изохронную передачу. На данный момент я безуспешно пробовал следующий код.

public void testCom(){
        if (!this.pret) return;

        ByteBuffer buffer = ByteBuffer.allocateDirect(18);
        buffer.rewind();
        // descriptor device
        int transfered = LibUsb.controlTransfer(handle,
                LibUsb.ENDPOINT_IN,LibUsb.REQUEST_GET_DESCRIPTOR,(short)0x0100,(short)0x0000,buffer,2000L);
        if (transfered < 0) throw new LibUsbException("Control transfer failed", transfered);
        System.out.println(transfered + " bytes sent");

        System.out.println("--- DEVICE DESCRIPTOR ---");
        System.out.println(byteBuffer2String(buffer));
        System.out.println();

        buffer = ByteBuffer.allocateDirect(9);
        // descriptor configuration
        transfered = LibUsb.controlTransfer(handle,
                LibUsb.ENDPOINT_IN,LibUsb.REQUEST_GET_DESCRIPTOR,(short)0x0200,(short)0x0000,buffer,2000L);
        if (transfered < 0) throw new LibUsbException("Control transfer failed", transfered);
        System.out.println(transfered + " bytes sent");

        System.out.println("--- CONFIGURATION DESCRIPTOR ---");
        System.out.println(byteBuffer2String(buffer));
        System.out.println();

        buffer = ByteBuffer.allocateDirect(8);
        // descriptor string
        transfered = LibUsb.controlTransfer(handle,
                LibUsb.ENDPOINT_IN,LibUsb.REQUEST_GET_DESCRIPTOR,(short)0x0300,(short)0x0409,buffer,2000L);
        if (transfered < 0) throw new LibUsbException("Control transfer failed", transfered);
        System.out.println(transfered + " bytes sent");

        System.out.println("--- DESCRIPTOR STRING ---");
        System.out.println(byteBuffer2String(buffer));
        System.out.println();

        System.out.println("TRYING TO GET DATA  ...");

        ByteBuffer bufferArgCmd = ByteBuffer.allocateDirect(16);
        /*bufferArgCmd.put((byte)0x21);
        bufferArgCmd.put((byte)0x09);
        bufferArgCmd.put((byte)0x02);
        bufferArgCmd.put((byte)0x00);
        bufferArgCmd.put((byte)0x00);
        bufferArgCmd.put((byte)0x00);
        bufferArgCmd.put((byte)0x08);
        bufferArgCmd.put((byte)0x00);*/

        bufferArgCmd.put((byte)0xA1);
        bufferArgCmd.put((byte)0x00);
        bufferArgCmd.put((byte)0x00);
        bufferArgCmd.put((byte)0x20);
        bufferArgCmd.put((byte)0xA1);
        bufferArgCmd.put((byte)0x00);
        bufferArgCmd.put((byte)0x00);
        bufferArgCmd.put((byte)0x20);
        bufferArgCmd.rewind();

        ByteBuffer bufferResCmd = ByteBuffer.allocateDirect(4);
        bufferResCmd.get();
        bufferResCmd.get();

        ByteBuffer bufferData = ByteBuffer.allocateDirect(32);


        ByteBuffer buffer81 = BufferUtils.allocateByteBuffer(32);
        // buffer81.put(data);
        Transfer transfer = LibUsb.allocTransfer(0);
        LibUsb.fillInterruptTransfer(transfer, handle, ENDPOINT_ID, bufferData,
            receiveData, null, 3000L);
        LibUsb.fillControlSetup(bufferArgCmd,(byte)0x21,(byte)0x09,(short)0x0200,(short)0x0,(short)0x08);
        // LibUsb.fillControlTransfer(transfer, handle, bufferArgCmd,
             //   sendData, null, 2000L);
        System.out.println("doing interrupt transfer to device");
        int result = LibUsb.submitTransfer(transfer);
        if (result != LibUsb.SUCCESS)
        {
            throw new LibUsbException("Unable to submit transfer", result);
        }

        System.out.println("Resultat du transfert : ");
        System.out.println(byteBuffer2String(bufferData));
    }

    // This callback is called after the ADB CONNECT message header is
    // sent and sends the ADB CONNECT message body.
    final TransferCallback receiveData = new TransferCallback()
    {
        @Override
        public void processTransfer(Transfer transfer)
        {
            System.out.println(transfer.actualLength() + " bytes received !!!! ");
            // write(handle, CONNECT_BODY, bodySent);
            // LibUsb.freeTransfer(transfer);
        }
    };

 // This callback is called after the ADB CONNECT message header is
    // sent and sends the ADB CONNECT message body.
    final TransferCallback sendData = new TransferCallback()
    {
        @Override
        public void processTransfer(Transfer transfer)
        {
            System.out.println("send command to device");
            // write(handle, CONNECT_BODY, bodySent);
            // LibUsb.freeTransfer(transfer);
        }
    };

Ответ ниже:

> Thermometre Pearl NC-7004 detecté !
Device Descriptor:
  bLength                 18
  bDescriptorType          1
  bcdUSB                1.10
  bDeviceClass             0 Per Interface
  bDeviceSubClass          0
  bDeviceProtocol          0
  bMaxPacketSize0          8
  idVendor            0x1941
  idProduct           0x8021
  bcdDevice             1.00
  iManufacturer            0
  iProduct                 0
  iSerial                  0
  bNumConfigurations       1

claim device handle
false
0
false
claim interface
18 bytes sent
--- DEVICE DESCRIPTOR ---
1201 1001 0000 0008 4119 2180 0001 0000 0001

9 bytes sent
--- CONFIGURATION DESCRIPTOR ---
0902 2200 0101 0080 32

4 bytes sent
--- DESCRIPTOR STRING ---
0403 0904 0000 0000

tentative de communication de la requete de données ...
doing interrupt transfer to device
Resultat du transfert :
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
java.lang.IllegalStateException: deviceHandlePointer is not initialized
        at org.usb4java.LibUsb.attachKernelDriver(Native Method)
        at usbDriver.Communication2.terminerCom(Communication2.java:313)
        at usbDriver.usbDriver.main(usbDriver.java:37)

Можете ли вы дать мне совет о том, как это выполнить?

person Belze88    schedule 25.10.2016
comment
Почему бы тебе не подождать немного перед выходом? Если вы используете асинхронный (изохронный — это нечто совершенно другое) передачи, вам нужно дождаться обратного вызова. - person dryman; 26.10.2016
comment
Я думаю, что я жду, указывая 200n мс тайм-аута, когда я заполняю контроль и прерывание передачи. Затем обычно, когда я отправляю его, должен быть отправлен управляющий пакет и вызвана функция обратного вызова sendData, но, по-видимому, нет, поскольку сообщение от этой функции не отображается... - person Belze88; 26.10.2016
comment
При заполнении перевода вы устанавливаете тайм-аут 3000. Это означает, что может пройти до 3 секунд, пока вы не узнаете, что произошло. Может быть, есть тайм-аут, но мы точно не знаем. И, кажется, вы вообще не отправляете передачу управления, потому что часть ее закомментирована. Я опубликую свое предположение в качестве ответа. - person dryman; 26.10.2016
comment
На самом деле я провел различные тесты: я не понимаю разницу между fillControlTransfer и fillControlSetup: fillControlSetup создает запрос, но без возможности передачи данных аргумента с этой настройкой. Тогда он все равно выполняет передачу управления? А для fillControlTransfer в документе LibUsb сказано, что 8 первых байт данных — это запрос. Так что я не знаю, какой из них я должен использовать. - person Belze88; 26.10.2016
comment
Как API говорит о fillControlSetup, это просто вспомогательный метод. С USB ничего не делает. Он просто заполняет первые 8 байтов буфера данными. Затем fillControlTransfer заполняет передачу данными, чтобы вы могли ее отправить. - person dryman; 26.10.2016

Несмотря на множество тестов с этой асинхронной передачей данных, все еще на исходе. Я полностью уверен, что моя просьба хороша. Хорошей новостью является то, что при обработке LibUsb.handleEventTimeOut вызывается функция обратного вызова передачи прерывания... когда наступает ее тайм-аут....

Это озадачивает, потому что команда, используемая в Windows с помощью driverWizard, работает отлично. Также я думаю, что это должно быть возможно сделать с моей текущей конфигурацией на Raspberry с LibUsb, поскольку синхронные передачи с устройством (получить дескриптор, конфигурацию...) работают нормально. Кроме того, даже если обратный вызов управления никогда не вызывается (вероятно, поскольку устройство ничего не отправляет обратно в канал управления), он заканчивается TIMEOUT с хорошей командой, а это ошибка 9 «недопустимая команда» с немного другой командой ... это Я думаю, хороший знак, он показывает, что устройство хотя бы немного реагирует на команду.

Но боюсь быть не в теме. Может ли это быть:

  • несовместимость из-за LibUsb/Raspberry, которая делает асинхронную передачу неуправляемой? Я не знаю, включают ли возможности raspberry usb асинхронные передачи (но это кажется вероятным, так как без него многие устройства не могли бы работать с raspberry!)
  • проблема с тем, как LibUsb обрабатывает асинхронную передачу? если устройство мало реагирует на команду, оно должно записать в канал 0x81, вызывая событие завершения передачи прерывания, нет? Это именно то, что происходит с прослушиванием канала в DriverWizard, хотя я не знаю, как именно это происходит.

Я открыт для всех идей прямо сейчас! Я еще попробую на следующей неделе, но если это не сработает, мне придется забыть об этом конкретном термометре.
Кстати, вы знаете термометры/гигрометры, которые легко настраиваются на малине и позволяют измерять время в реальном времени (каждую минуту)? и передача данных?

person Belze88    schedule 28.10.2016
comment
Примечание к синхронному/асинхронному: libusb synchronoues API использует внутреннюю асинхронность, поэтому, если синхронная работа работает, должен быть способ. Может быть, заглянуть в исходники libusb, чтобы узнать, что они делают по-другому? Кроме этого у меня больше нет идей. - person dryman; 02.11.2016

На самом деле, этот код работает!

private void baseRequest(){

        LibUsb.clearHalt(handle, (byte)0x0);
        LibUsb.clearHalt(handle, (byte)0x81);

        int result;
        int count = 0;

        bufferData = ByteBuffer.allocateDirect(32);
        Transfer interruptTransfer = LibUsb.allocTransfer(0);
        LibUsb.fillInterruptTransfer(interruptTransfer , handle, ENDPOINT_ID, bufferData,receiveBaseRequest, null, 200L);
        //bufferData.rewind();


        LibUsb.unlockEvents(contexte);
        LibUsb.submitTransfer(interruptTransfer);

        bufferControl= ByteBuffer.allocateDirect(16);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x00);
        // now the data
        bufferControl.put((byte)0xa1);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x20);
        bufferControl.put((byte)0xa1);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x00);
        bufferControl.put((byte)0x20);
        bufferControl.rewind();
        Transfer controlTransfer = LibUsb.allocTransfer(0);
        //bufferControl.rewind();
        LibUsb.fillControlSetup(bufferControl,(byte)0x21,(byte)0x09,( short)0x0200,(short)0x0000,(short)0x08);
        LibUsb.fillControlTransfer(controlTransfer, handle, bufferControl,sendBaseRequest, null, 200L);
        //bufferControl.rewind();
        controlTransfer.setEndpoint((byte)0x00);
        display(byteBuffer2String(controlTransfer.buffer()));
        result = LibUsb.submitTransfer(controlTransfer);

        // TODO : voir pour free les transferts
    }

@Dryman: спасибо за ваши советы, я думаю, что моя главная ошибка заключалась в том, чтобы выполнить fillControlTransfer до fillControlSetup. На самом деле это не работает таким образом, что fillControlSetup должен быть выполнен до fillControlTransfer (я заметил это, читая исходный код для synchronousControlTransfer). Во всяком случае, теперь я могу эффективно общаться с устройством, спасибо за совет!

person Belze88    schedule 24.11.2016

Я думаю, что это должно выглядеть примерно так:

    ByteBuffer bufferControl= ByteBuffer.allocateDirect(16);
    // leaving out 8 byte for setup packet
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x00);

    // now the data
    bufferControl.put((byte)0xA1);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x20);
    bufferControl.put((byte)0xA1);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x00);
    bufferControl.put((byte)0x20);
    bufferControl.rewind();

    // this is for the data to read
    ByteBuffer bufferData = ByteBuffer.allocateDirect(32);

    Transfer controlTransfer = LibUsb.allocTransfer(0);
    LibUsb.fillControlSetup(bufferControl,(byte)0x21,(byte)0x09, short)0x0200,(short)0x0,(short)0x08);
    LibUsb.fillControlTransfer(controlTransfer, handle, bufferControl, sendData, null, 2000L);
    int result = LibUsb.submitTransfer(controlTransfer);
    if (result != LibUsb.SUCCESS)
    {
        throw new LibUsbException("Unable to submit control transfer", result);
    }

    System.out.println("doing interrupt transfer to device");
    Transfer interruptTransfer = LibUsb.allocTransfer(0);
    LibUsb.fillInterruptTransfer(interruptTransfer , handle, ENDPOINT_ID, bufferData,
        receiveData, null, 3000L);
    int result = LibUsb.submitTransfer(interruptTransfer);
    if (result != LibUsb.SUCCESS)
    {
        throw new LibUsbException("Unable to submit interrupt transfer", result);
    }
    Thread.Sleep(5000);
person dryman    schedule 26.10.2016
comment
Спасибо, вечером проверю! - person Belze88; 26.10.2016
comment
Я проверял, но это не работает прямо сейчас. в конце метода bufferData по-прежнему полностью равен 0. Странно то, что даже если передача кажется успешной (возвращает успех LibUsb), функции обратного вызова не вызываются (они должны отображать сообщения, но я ничего не получаю). Я не понимаю: что бы ни случилось (успех, сбой или тайм-аут), когда передача отправлена, функция обратного вызова должна вызываться в конце, нет? - person Belze88; 26.10.2016
comment
Да, обратный вызов должен быть вызван через некоторое время после вызова submitTransfer и самое большее после тайм-аута, указанного в передаче. Но submitTransfer не будет блокироваться до тех пор. - person dryman; 27.10.2016

После нескольких тестов и вариаций он все еще не работает ...

Очень раздражает, что функции обратного вызова не вызываются. Действительно ли функция обратного вызова sendData должна вызываться при отправке controlTransfer, нет? но на тесте он просто никогда не вызывается. Действительно ли осуществлена ​​передача управления? тайна ...

Кроме того, если я хорошо разбираюсь, на USB нет прослушивателя. Устройство может говорить только по запросу. Это означает, что после завершения передачи управления я должен выполнить передачу прерывания на моей конечной точке данных, 0x81, с буфером данных 32 байта, но как именно я могу указать устройство для отправки обратно данных с этим? я читал о токене направления, так что первый байт данных при прерывании должен иметь определенное значение, указывающее, что устройство эффективно записывает в этот буфер и отправляет данные обратно?

person Belze88    schedule 26.10.2016
comment
Направление задается конечной точкой. 0x81 — это конечная точка чтения. Вы можете только читать из него. И он будет читать все, что есть в буфере устройства. Если устройство не хочет вам ничего отправлять, буфер пуст и время передачи истекает. - person dryman; 27.10.2016
comment
Хорошо, действительно, спасибо за эту точность. Сегодня вечером я проверю цикл обработки событий, я видел, что он использовался в этой ссылке: falsinsoft.blogspot.fr/2015/02/. Я надеюсь, что это действительно запускает мою функцию обратного вызова. Особенно тот факт, что обратный вызов, обрабатывающий передачу управления, не вызывается, является серьезной проблемой на данный момент. - person Belze88; 27.10.2016