Endian-Problem: C-Funktion? unter Linux
-
Hallo,
Ich bin dabei das USBIP-Projekt auf ein Mikroprozessor zu portieren.
Dabei ist mir vermutlich ein Endian-Problem aufgefallen.
An einer Stelle wo ein normaler PC den Wert 1 hat, steht im Mikroprozessor der Wert 16777216.
Gibt es eine einfache c-Funktion um die Byte-Order zu ändern?
Network-Byte-Order klappt wohl nicht.Ich hoffe ihr könnt mir helfen.
-
Eine Standard-C-Funktion gibt es dafür meines Wissens nicht. Unter Linux gibt es htonl(), htons(), ntohl() und ntohs(), um von Host- in Network-Byte-Order zu konvertieren und umgekehrt.
Bibliotheken wie SDL bieten auch Funktionen oder Makros an, um Bytes zu swappen. So etwas kann man sich mit ein paar Schiebeoperationen auch leicht selbst schreiben, z. B. für 32 Bit (aus SDL, steht also unter der LGPL):
static __inline__ Uint32 SDL_Swap32(Uint32 x) { return((x<<24)|((x<<8)&0x00FF0000)|((x>>8)&0x0000FF00)|(x>>24)); }
Eventuell bietet dein Mikroprozessor ja auch eine Instruktion, um Bytes zu swappen.
-
erstmal vielen Dank für deine Antwort.
htonl(), htons(), ntohl() und ntohs() sind in dem Fall scheinbar ohne Wirkung.
So sieht doch die Syntax aus oder irre ich mich da?
uint32_t htonl(Variable);
Wie nutze ich denn Bibliotheken?
Um diese Struktur geht es, meiner Meinung nach:
struct op_devlist_reply { uint32_t ndev; /* followed by reply_extra[] */ } __attribute__((packed));
Hier ist ein ein Aufruf der schon deklariert ist und wohl genau das machen soll.
#define PACK_OP_DEVLIST_REPLY(pack, reply) do {\ pack_uint32_t(pack, &(reply)->ndev);\ } while (0)
void pack_uint32_t(int pack, uint32_t *num) { uint32_t i; if (pack) i = htonl(*num); else i = ntohl(*num); *num = i; }
Und hier wirkt sich der falsche Wert von rep.ndev auf die Schleife aus.
PACK_OP_DEVLIST_REPLY(0, &rep); dbg("exportable %d devices", rep.ndev); printf("rep.ndev %d\n",rep.ndev); for (unsigned int i=0; i < rep.ndev; i++) { char product_name[100]; char class_name[100]; struct usb_device udev; pack_uint32_t(1,&rep); printf("rep.ndev %d\n",rep.ndev); bzero(&udev, sizeof(udev)); ret = usbip_recv(sockfd, (void *) &udev, sizeof(udev)); printf("query_exported_devices: usbip_recv_2 : ret = %d\n",ret); if (ret < 0) { err("recv usb_device[%d]", i); return -1; } pack_usb_device(0, &udev); usbip_names_get_product(product_name, sizeof(product_name), udev.idVendor, udev.idProduct); usbip_names_get_class(class_name, sizeof(class_name), udev.bDeviceClass, udev.bDeviceSubClass, udev.bDeviceProtocol); info("%8s: %s", udev.busid, product_name); info("%8s: %s", " ", udev.path); info("%8s: %s", " ", class_name); for (int j=0; j < udev.bNumInterfaces; j++) { struct usb_interface uinf; ret = usbip_recv(sockfd, (void *) &uinf, sizeof(uinf)); printf("query_exported_devices: usbip_recv_3 : ret = %d\n",ret); if (ret < 0) { err("recv usb_interface[%d]", j); return -1; }
Ich hoffe ich hab einigermaßen darstellen können.
Vielleicht sieht ja ein Expert sofort, wo das Problem ist.
-
Ja, so sieht die Syntax für htonl() aus. Das mit der Bibliothek war nur ein Beispiel, wo man sich Ideen für Code zum Swappen von Bytes holen kann. Die gesamte SDL-Bibliothek einzubinden, wäre wahrscheinlich etwas zu viel des Guten, zumal sie vielleicht erst noch portiert werden müsste.
Da ja vor der for-Schleife die fehlerhafte Variable von Network- in Host-Byte-Order konvertiert wird, vermute ich, dass die Variable vor der Konvertierung einfach nicht in Network-Byte-Order vorliegt. An welcher Stelle wird diese Variable denn gesetzt, bzw. woher wird ihr Wert bezogen?
-
Das ist der Code vor der for-Schleife.
static int query_exported_devices(int sockfd) { int ret; struct op_devlist_reply rep; uint16_t code = OP_REP_DEVLIST; // bzero(&rep, sizeof(rep)); printf("rep: %d\n",rep); ret = usbip_send_op_common(sockfd, OP_REQ_DEVLIST, 0); printf("query_exported_devices: usbip_send_op_common : ret = %d\n",ret); if (ret < 0) { err("send op_common"); return -1; } ret = usbip_recv_op_common(sockfd, &code); printf("query_exported_devices: usbip_recv_op_common : ret = %d\n",ret); if (ret < 0) { err("recv op_common"); return -1; } ret = usbip_recv(sockfd, (void *) &rep, sizeof(rep)); printf("rep: %d\n",rep); printf("query_exported_devices: usbip_recv : ret = %d\n",ret); if (ret < 0) { err("recv op_devlist"); return -1; } PACK_OP_DEVLIST_REPLY(0, &rep); dbg("exportable %d devices", rep.ndev); printf("rep.ndev %d\n",rep.ndev);
Wo genau der Wert für rep.ndev herkommt, kann ich dir ehrlich gesagt nicht sagen.
Hab nur die Deklaration, die ich schon gepostet, hab gefunden.
-
Der Wert für rep.ndev kommt über usbip_revc() und usbip_xmit() direkt vom Socket. Der Fehler muss also auf der sendenden Seite liegen. Bei Gelegenheit werde ich mir das mal genauer angucken, habe jetzt leider nicht die Zeit dazu.
Edit: Der Wert wird in der Funktion send_reply_devlist() gesendet. Ermittelt wird er über die Anzahl der Elemente irgendeiner Liste. Aber auch hier wird der Wert richtigerweise vor dem Senden von Host- in Network-Byte-Order konvertiert. Meines Erachtens kann es also am USB/IP-Code selbst nicht liegen.
Auf was für eine Plattform portierst du den Code denn? Vielleicht macht die Implementation der Standard-C-Bibliothek Probleme? Eventuell wird ja die Byte-Order des Systems nicht richtig ermittelt, so dass htonl() und ntohl() nicht richtig arbeiten.
-
Oh, super.
Ich komm namlich absolut nicht weiter.Portiert wird das ganze auf eine ARM9-Plattform.
Der Mikroprozessor nennt sich Picocom1.