[solved] Problem mit libusb und dem empfangen größerer datenmengen



  • Hi
    Ich schreibe ein kleines Tool um mit einem ARM-Controller per USB zu kommunizieren. Die ARM-Firmware ist soweit fertig und wird mit einem Windows-Tool bereits erfolgreich verwendet. Ich möchte nun ein Tool schreiben um auch unter Linux damit arbeiten zu können.

    Als lib verwende ich die libusb 1.0 unter Kubuntu 11.04 (64Bit) (Linux 2.6.38-10-generic)

    Ich öffne das gerät wie folgt:
    Update: Hatte libusb_claim_interface nicht aufgerufen, ändert aber nichts am problem…

    bool CUSB::SelectDevice(libusb_device *dev)
    {
     if(dev == NULL)
     {
      if(this->devicehandle != NULL)
      {
       libusb_release_interface(this->devicehandle, 0);
       libusb_close(this->devicehandle);
      }
    
      this->device       = NULL;
      this->devicehandle = NULL;
      return true;
     }
    
     this->device = dev;
    
     int retval = libusb_open(this->device, &this->devicehandle);
     if(retval != 0) //Fehler!
     {
      fprintf(stderr,"\e[1;31mlibusb_open - error %i\e[0m\n",retval);
      this->device = NULL;
      this->devicehandle = NULL;
     }
    
     retval = libusb_claim_interface(this->devicehandle, 0); //interface 0 verwenden
     if(retval != 0) //Fehler!
     {
      fprintf(stderr,"\e[1;31mlibusb_claim_interface - error %i\e[0m\n",retval);
    
      if(this->devicehandle != NULL) libusb_close(this->devicehandle);
      this->device = NULL;
      this->devicehandle = NULL;
     }
    
     return true;
    }
    

    In der CUSB-Klasse lese ich maximal ein Paket ein, wenn ich mehr einlesen möchte wird das in einer abgeleiteten klasse aufgebröselt.

    int CUSB::Read(unsigned char endpoint, unsigned char *data, int length)
    {
     int retval; //zwischenspeicher für rückgabewerte
    
     //prüfen ob von diesem endpoint überhaupt gelesen werden kann
     if(!(endpoint & 0x80))
     {
      fprintf(stderr,"\e[1;33mCUSB::Read: cannot read from endpoint 0x%02X\e[0m\n", endpoint);
      return 0; //nichts gelesen
     }
    
     //Prüfen ob das gerät geöffnet wurde
     if(this->device == NULL || this->devicehandle == NULL)
     {
      fprintf(stderr,"\e[1;33mCUSB::Read: No device selected\e[0m\n");
      return 0; //schrieben nicht möglich
     }
    
     //Prüfen ob überhaupt so viele Daten gelesen werden können
     retval = this->GetMaxPacketLength(endpoint);
     if(retval < length)
     {
      fprintf(stderr,"\e[1;33mCUSB::Read: cannot read more %i bytes\e[1;30m(length-argument was %i bytes)\e[0m\n", retval, length);
      return 0; //ist zum scheitern verurteilt
     }
    
     ///und lesen
     int transferred;
     retval = libusb_bulk_transfer(this->devicehandle, endpoint, data, length, &transferred, CUSB_TIMEOUT); //! timeout = 0 => unendlich
     if(retval != 0)
     {
      fprintf(stderr,"\e[1;31mlibusb_bulk_transfer - error %i\e[0m\n", retval);
      return 0; //irgendwas ist passiert, die daten sind ungültig
     }
    
     return transferred;
    }
    

    Der vollständige Lesevorgang sieht dann so aus:

    int CARMBoard::ReadFlashFromMicrocontroller(const char *path)
    {
     //Nun muss geprüft werden wie viel daten geleichzeitig gelesen werden dürfen
     int maxpsize = this->CUSB::GetMaxPacketLength(ENDP_RECEIVE);
     if(maxpsize <= 0)
     {
      return -1; // entweder fehler (-1), oder paketgröße zu klein (0)
     }
    
     //Zur richtigen Adresse springen
     if(this->SetWorkAddress(ARMBOARD_APPLICATION_ADDRESS) == -1)
     {
      return -1;
     }
    
     //ankündigen, dass gleich Daten kommen
     //Befehl zum lesen schicken
     unsigned char command[] = {CMD_READ_INT,  (unsigned char)(ARMBOARD_FLASHSIZE>> 0),
                                               (unsigned char)(ARMBOARD_FLASHSIZE>> 8),
                                               (unsigned char)(ARMBOARD_FLASHSIZE>>16),
                                               (unsigned char)(ARMBOARD_FLASHSIZE>>24)}; //Befehlsfolge
    
     int retval = this->CUSB::Write(ENDP_TRANSMIT_CMD, command, sizeof(command));
     if(retval != sizeof(command))
     {
      fprintf(stderr, "\e[1;31msending the command to read data from the internel flash failed.\e[0m\n");
      return -1;
     }
    
     //Daten lesen
     unsigned char *buffer = new unsigned char[ARMBOARD_FLASHSIZE];
     if(buffer == NULL)
     {
      fprintf(stderr, "\e[1;31munexpected error in CARMBoard::ReadFlashFromMicrocontroller. new failed!\e[0m\n");
      return -1;
     }
     unsigned char *currbufferpos = buffer; //aktuelle Position im buffer
     int            packetsize    = maxpsize; //Anzahl der zu verschickenden Daten
     int            received      = 0; //bereits empfangene Daten
    
     while(ARMBOARD_FLASHSIZE > received)
     {
      if(packetsize > ARMBOARD_FLASHSIZE - received) packetsize = ARMBOARD_FLASHSIZE - received; //die letzen paar bytes
    
      //Daten empfangen
      retval = this->CUSB::Read(ENDP_RECEIVE, currbufferpos, packetsize);
      if(retval != packetsize)
      {
       if(retval > 0) WriteBufferToFile("./rescued.txt", (const char*)buffer, received+retval); //es konnte zwar nicht alles gelesen werden, aber diese Daten könnten noch zu gebrauchen sein…
    
       fprintf(stderr, "\e[1;31mreceiving data from internel flash failed.\e[0;31m Just got %i from %i bytes\e[0m\n", retval, packetsize);
       if(buffer != NULL) delete[] buffer; //buffer freigeben
       return -1;
      }
    
      //Berechnungen anstellen
      currbufferpos += packetsize;
      received      += packetsize;
    
      //callback aufrufen
      this->ReadFlashFromMicrocontroller_CBProgress(ARMBOARD_FLASHSIZE, received);
     }
    
     //Jetzt den buffer in eine Datei schreiben und fertig
     WriteBufferToFile(path, (const char*)buffer, ARMBOARD_FLASHSIZE);
    
     if(buffer != NULL) delete[] buffer; //Buffer freigeben
     return 0;
    }
    

    --EDIT--
    Vereinfacht dargestellt:

    • per libusb_open das gerät geöffnet
    • per libusb_bulk_transfer zu einem endpoint den Befehl zum setzen der arbeitsadresse incl Adresse geschickt
    • per libusb_bulk_transfer zu einem endpoint den Befehl zum lesen von n bytes geschickt
    • per libusb_bulk_transfer die Daten einlesen

    --/EDIT--

    Wenn ich z.B. nur 1KiB einlese funktioniert alles ohne Probleme, aber bei 2KiB kommt es schon zu dem Problem dass ich irgendwann kein ganzes Paket von hier 64Byte bekomme sondern ein paar Bytes weniger.
    Das Problem tritt auch dann noch auf wenn ich per usleep nach jeden Lesevorgang etwas warte.

    Hat jemand 'ne Idee woran das liegen könnte? Wo ich suchen muss? Ich hatte mir schon überlegt einfach das Paket neu anzufordern, allerdings bekomme ich dann von µC die nächsten 64Byte wodurch ich dann quasi diese 64Byte verloren habe…

    Update: Problem gefunden und gelöst:

    libUSB-Doku: libusb may have to split your transfer into a number of chunks to satisfy underlying O/S requirements,[..]

    Die korrigierte und funktionierende while-Schleife zum einlesen der Daten:

    ///...
    
     while(ARMBOARD_FLASHSIZE > received)
     {
      if(packetsize > ARMBOARD_FLASHSIZE - received) packetsize = ARMBOARD_FLASHSIZE - received; //die letzen paar bytes
    
      //Daten empfangen
      retval = this->CUSB::Read(ENDP_RECEIVE, currbufferpos, packetsize);
    
      if(retval <= 0) //fehler bei der übertragung
      {
       fprintf(stderr, "\e[1;31mreceiving data from internel flash failed.\e[0m\n");
       if(buffer != NULL) delete[] buffer; //buffer freigeben
       return -1;
      }
    
      //Berechnungen anstellen
      currbufferpos += retval;
      received      += retval;
    
      //callback aufrufen
      this->ReadFlashFromMicrocontroller_CBProgress(ARMBOARD_FLASHSIZE, received);
     }
    
    ///...
    


  • Ich dachte mir schon das das daran liegt.
    Ist sowie mit den Berkeley-Sockets.

    Danke für die Lösung!


Log in to reply