[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!