4 votes

LibUSB C++ Le format de transfert USB diffère

J'utilise stack overflow depuis longtemps maintenant, et pour la plupart des problèmes, une solution est déjà disponible. C'est la première fois que je n'arrive pas à trouver une solution avec le web. J'espère que quelqu'un a la réponse au problème suivant.

Introduction Je travaille actuellement sur un projet qui devrait être capable d'exécuter une commande et d'agir en fonction de sa réponse. Ce projet tourne sur un système debian dans une application console c++. Afin de pouvoir exécuter de telles commandes, j'ai essayé d'utiliser la commande LibUSB bibliothèque.

Le problème Chaque fois que des paquets sont envoyés, il ne renvoie pas de réponse valide telle que décrite dans la documentation du matériel. Un outil par défaut est disponible qui déclenche une commande de calibration, j'ai reniflé ces paquets avec Wireshark Mais la structure des appels d'interruption OUT de l'outil de callibration diffère de celle générée par LibUSB, ce qui (je pense) fait que la commande n'est pas exécutée.

La documentation fournit l'une des commandes suivantes, qui devrait exécuter un contrôle de diagnostic renvoyant 5 octets de données.

[0] Header: 0x02   
[1] Command: 0x4C  
[2] Byte to send: 0x02 (N bytes to send, Argument + data size)   
[3] Argument: 0x09  
[4] Data: 0x00

La réponse doit avoir le format suivant :

[0] Header: 0x02  
[1] Command: 0x4C  
[2] Byte to send: 0x03 (N bytes to send, Argument + data size)  
[3] Argument: 0x09  
[4] Processing result: D-1  
[5] Diagnostic result: D-2

D-1 soit 0x01 : Normal ou 0x00 Erreur D-2 soit 0x00 : normal ou non 0x00, code d'erreur lié.

Ce qui a été essayé jusqu'à présent

  • Types de transfert :
    • Synchrone :
    • Libusb_bulk_transfer
    • Libusb_control_transfer
    • libusb_interrupt_transfer
    • Asynchrone :
    • Libusb_fill_bulk_transfer
    • Libusb_fill_control_transferts
    • Libusb_fill_interrupt_transfer

J'ai essayé les deux implémentations, asynchrone et synchrone, de la bibliothèque LibUSB. Pour le transfert de contrôle, j'ai essayé de changer aléatoirement les variables après que les moyens les plus logiques de les remplir aient été épuisés, sans succès, comme on pouvait s'y attendre. Puisque les résultats trouvés dans le reniflage de paquets indiquaient clairement que des appels INTERRUPT étaient effectués.

Interfaces : Le matériel possède deux interfaces. L'interface 0 qui contient OUT 0x02 et IN 0x81, et l'interface 1 qui contient OUT 0x04 et IN 0x83. Le reniflage de l'appel d'interruption USB vers le dispositif déclenché par l'outil a permis d'établir que l'interface 1 était utilisée pour la commande de diagnostic. (J'ai également essayé l'interface 0 avec IN et OUT, mais cela n'a pas fonctionné.

Reniflage de paquets avec Wireshark

Résultats du reniflage de paquets Requêtes et réponses générées par l'outil : IMG : Interruption OUT (J'ai marqué le passage où la commande est effectivement fournie) IMG : Interruption IN response Ce code fonctionne réellement et renvoie l'ensemble de données attendu dans son emplacement de données. (comme décrit ci-dessus, le format de retour est correct, 0x01 et 0x00).

Requête et réponse générées avec le code d'utilisation de LibUSB : IMG : Interruption OUT IMG : Interruption IN réponse

Oui, j'ai également essayé de régler la mémoire tampon sur une taille de 64, la taille maximale de la mémoire tampon pour le matériel. Malheureusement, cela n'a pas fonctionné. Comme on le voit clairement, les deux demandes sont très différentes, est-ce que j'utilise la mauvaise méthode de transfert ? Existe-t-il un autre format supporté dans lequel vous pouvez envoyer des commandes ?

Extrait de code utilisé : L'extrait de code est un peu dépassé, j'ai essayé de le réécrire/éditer plusieurs fois, les dernières implémentations ayant été utilisées à partir d'exemples en ligne.

#define USB_VENDOR_ID       <VENDOR_ID>/* USB vendor ID used by the device
                                         * 0x0483 is STMs ID
                                         */
#define USB_PRODUCT_ID      <PRODUCT_ID>      /* USB product ID used by the device */
#define USB_ENDPOINT_IN     (LIBUSB_ENDPOINT_IN  | 0x83)   /* endpoint address */
#define USB_ENDPOINT_OUT    (LIBUSB_ENDPOINT_OUT | 0x04)   /* endpoint address */
#define USB_TIMEOUT         3000        /* Connection timeout (in ms) */
#define INTERFACE_NO        1

static libusb_context *ctx = NULL;
static libusb_device_handle *handle;

static uint8_t receiveBuf[64];
uint8_t transferBuf[64];

uint16_t counter=0;

int main(int argc, char **argv) {
    libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
    libusb_device_handle *dev_handle; //a device handle
    libusb_context *ctx = NULL; //a libusb session
    int r; //for return values
    ssize_t cnt; //holding number of devices in list
    r = libusb_init(&ctx); //initialize the library for the session we just declared
    if(r < 0) {
        qDebug()<<"Init Error "<<r<<endl; //there was an error
        return 1;
    }
    libusb_set_debug(ctx, 4); //set verbosity level to 3, as suggested in the documentation

    cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
    if(cnt < 0) {
        qDebug()<<"Get Device Error"<<endl; //there was an error
        return 1;
    }
    qDebug()<<cnt<<" Devices in list."<<endl;

    dev_handle = libusb_open_device_with_vid_pid(ctx, 0x0AFA, 0x7D3); //these are vendorID and productID I found for my usb device
    if(dev_handle == NULL)
        qDebug()<<"Cannot open device"<<endl;
    else
        qDebug()<<"Device Opened"<<endl;
    libusb_free_device_list(devs, 1); //free the list, unref the devices in it

    unsigned char *data = new unsigned char[5] { 0x02, 0x4C, 0x02, 0x09, 0 }; //data to write
    data[0]= 0x02;data[1]= 0x4C;data[2]=0x02;data[3]=0x09; data[4]= 0; //some dummy values

    int actual; //used to find out how many bytes were written
    if(libusb_kernel_driver_active(dev_handle, INTERFACE_NO) == 1) { //find out if kernel driver is attached
        qDebug()<<"Kernel Driver Active"<<endl;
        if(libusb_detach_kernel_driver(dev_handle, INTERFACE_NO) == 0) //detach it
            qDebug()<<"Kernel Driver Detached!"<<endl;
    }
    r = libusb_claim_interface(dev_handle, INTERFACE_NO); //claim interface 0 (the first) of device (mine had jsut 1)
    if(r < 0) {
        qDebug()<<"Cannot Claim Interface"<<endl;
        return 1;
    }
    qDebug()<<"Claimed Interface"<<endl;

    for(int i = 0; i != sizeof(data); i++) {
        fprintf(stderr, "[%d] - %02x\n", i, data[i]);
    }
    qDebug()<<"Writing Data..."<<endl;
    r = libusb_bulk_transfer(dev_handle, (USB_ENDPOINT_OUT | LIBUSB_ENDPOINT_OUT), data, sizeof(data), &actual, 0); //my device's out endpoint was 2, found with trial- the device had 2 endpoints: 2 and 129
    if(r == 0 && actual == sizeof(data)) //we wrote the 4 bytes successfully
        qDebug()<<"Writing Successful!"<<endl;
    else
        qDebug()<<"Write Error"<<endl;
        fprintf(stderr, "Error Writing: %s", libusb_strerror(static_cast<libusb_error>(r)));

    r = libusb_release_interface(dev_handle, INTERFACE_NO); //release the claimed interface
    if(r!=0) {
        qDebug()<<"Cannot Release Interface"<<endl;
        return 1;
    }
    qDebug()<<"Released Interface"<<endl;

    libusb_close(dev_handle); //close the device we opened
    libusb_exit(ctx); //needs to be called to end the

    delete[] data; //delete the allocated memory for data
    return 0;
}

J'espère que quelqu'un est capable et désireux de m'aider, car cela fait trois jours que je travaille sur ce sujet et je n'ai toujours pas trouvé de solution logique à ce problème.

Merci d'avance !

~ Mark

2voto

DiabetiMark Points 41

Merci de votre réponse ! J'ai trouvé une solution au problème ! Cela n'a rien à voir avec l'utilisation de C / C++. Désolé pour le code un peu désordonné. Je l'ai écrit plusieurs fois et l'ordre n'était donc pas ma priorité, mais je le garderai à l'esprit pour un éventuel futur post sur StackOverflow. Bien que résolu, j'ai ajouté les résultats du sniffing des deux paquets entrant et sortant, en espérant que cela puisse aider d'autres personnes ayant le même problème.

Quel était le problème ?
Ainsi, la capture de l'outil indique que les 64 derniers bits sont la charge utile de la requête et ses données, tant pour OUT que pour IN. (Comme je l'ai déjà dit, j'ai essayé d'allouer des tableaux d'une taille de 64 et de placer les premiers emplacements avec les données nécessaires à l'opération. Quant aux autres emplacements, ils ont été remplis avec les données restantes qui se trouvaient à ces adresses de mémoire allouées.

Qu'est-ce que j'ai fait pour résoudre le problème ?
J'ai donc procédé comme suit. Après avoir initialisé un tableau et lui avoir attribué une taille de 64, j'ai mis tous les emplacements alloués à 0 avec la commande memset, afin que le tableau soit complètement vidé des données restantes. Cela m'a laissé un tableau propre dans lequel j'ai pu définir les variables nécessaires à la commande que je voulais envoyer. (Voir l'extrait suivant)

// Initialize array of 64 bytes.
    uint8_t *data = new uint8_t[64];
    memset(data, 0x00, 64);
    data[0] = 0x02; data[1] = 0x4C; data[2] = 0x01; data[3] = 0x17;

J'ai un peu modifié le code pour qu'il soit plus lisible, voici le code que j'ai utilisé et qui fonctionne ! J'espère que d'autres personnes trouveront ces informations utiles.

//*** DEPENDENCIES *************************************************************
// QT
#include <QCoreApplication>
#include <QtCore/QDebug>
// Others
#include <libusb.h>
#include <iostream>

//*** VARIABLES ****************************************************************
#define USB_VENDOR_ID      <VENDOR_ID_GOES_HERE>
#define USB_PRODUCT_ID     <PRODUCT_ID_GOES_HERE>
#define USB_ENDPOINT_OUT    0x04
#define USB_ENDPOINT_IN     0x83

#define INTERFACE_NO        0x01

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    libusb_device *dev;
    libusb_device_handle *dev_handle;
    libusb_context *ctx = NULL;

    //*** INITIALIZATION *******************************************************
    uint r = libusb_init(&ctx);
    // Check if initiated succesfully
    if ( r < 0 )                            { qDebug() << "Init error."; return 1; }
    libusb_set_debug(ctx, 4);

    dev_handle = libusb_open_device_with_vid_pid(ctx, USB_VENDOR_ID, USB_PRODUCT_ID);

    if (dev_handle == NULL) { qDebug() << "Could not open device."; return 1;}
    qDebug() << "Device opened succesfully!";

    // Check if kernel driver, detach
    if(libusb_kernel_driver_active(dev_handle, INTERFACE_NO) == 1) {
            qDebug() << "Kernel Driver Active";
            if(libusb_detach_kernel_driver(dev_handle, INTERFACE_NO) == 0) {
               qDebug() << "Kernel Driver Detached";
        }
    }

    // Claim interface
    r = libusb_claim_interface(dev_handle, INTERFACE_NO);
    if ( r < 0 ) {
        qDebug() << "Could not claim interface.";
        return 1;
    }
    qDebug() << "Interface claimed.";

    //*** EXECUTION OF USB TRANSFERS *******************************************

    // Prepare command
    int actual_written;
    // Initialize array of 64 bytes.
    uint8_t *data = new uint8_t[64];
    memset(data, 0x00, 64);
    data[0] = 0x02; data[1] = 0x4C; data[2] = 0x01; data[3] = 0x17;

    qDebug() << "================= OUT ==============================";
    //*** ATTEMPT TO WRITE COMMAND *********************************************
    r = libusb_bulk_transfer(dev_handle,
                             USB_ENDPOINT_OUT,
                             data, 64,
                             &actual_written,
                             10000);

    qDebug() << "OUT status: " << libusb_strerror(static_cast<libusb_error>(r));
    if (r == 0 && actual_written == 64) {
        qDebug() << "Succesfully written!";
    } else {
        qDebug() << "||" << r << "||"<< actual_written << "||"
                 << "Could not write.";
    }
    qDebug() << "================== IN ===============================";

    //*** ATTEMPT TO READ FEEDBACK *********************************************
    // Initialize array of 64 bytes.
    uint8_t *feedback = new uint8_t[64];
    memset(feedback, 0x00, 64);

    int actual_received;
    r = libusb_bulk_transfer(
                dev_handle,
                USB_ENDPOINT_IN,
                feedback,
                64,
                &actual_received,
                0);

    qDebug() << "IN status: " << libusb_strerror(static_cast<libusb_error>(r));
   if(r == 0 && actual_received == 64) {
            qDebug("\nRetrieval successful!");
            qDebug("\nSent %d bytes with string: %s\n", actual_received, feedback);
    } else {
       qDebug() << actual_received << "||" <<feedback << "||"
                << "Could not read incoming data. ||";
   }

    for( int m = 0; m < 64; m++)
    {
        fprintf(stderr, "[%d] - %02x\n", m, feedback[m]);
    }

    if (feedback[4] != 0x01) {
        qDebug() << "Unsuccesful offset adjustment.";
        return -1;
    }

    // Further code should go here.

    //*** FREEING USB **********************************************************
    // Releasing interface
    r = libusb_release_interface(dev_handle, INTERFACE_NO);
    if ( r < 0 )        { qDebug() << "Could not release interface."; return 1; }
    qDebug() << "Interface released.";

    libusb_close(dev_handle);
    libusb_exit(ctx);
    delete[] data;
    delete[] feedback;

    qDebug() << "End of main";
    return 0;
}

Thomas et David, merci beaucoup !

~ Mark

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X