Forum: PC-Programmierung Linux socketCan Interrupthandler mehrere CANs


von Johannes (Gast)


Lesenswert?

Hallo,
ich habe unter linux socket can (4 CAN-Nodes) implementiert.
wenn eine message reinkommt, rufe ich einen interrupt auf. Diese habe 
ich mit pthread erstellt.
1
    ret = pthread_create(&thread_id, NULL, (void*)pVCan1Rx, NULL);
2
    ret = pthread_create(&thread_id, NULL, (void*)pVCan2Rx, NULL);
3
    ret = pthread_create(&thread_id, NULL, (void*)pVCan3Rx, NULL);
4
    ret = pthread_create(&thread_id, NULL, (void*)pVCan4Rx, NULL);


die threads sehen dann wie folgt aus
1
void * pVCan1Rx(__attribute__((unused)) void * pData)
2
{
3
    int s, i;
4
    int nbytes;
5
    struct sockaddr_can addr;
6
    struct ifreq ifr;
7
    struct can_frame frame;
8
9
    uint16 msgHandle_u16 = 0;
10
11
    char *ifname = "vcan0";
12
13
14
    if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
15
        perror("Socket");
16
        return 1;
17
    }
18
19
    strcpy(ifr.ifr_name, ifname );
20
    ioctl(s, SIOCGIFINDEX, &ifr);
21
22
    memset(&addr, 0, sizeof(addr));
23
    addr.can_family = AF_CAN;
24
    addr.can_ifindex = ifr.ifr_ifindex;
25
26
    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
27
        perror("Bind");
28
        return 1;
29
    }
30
31
    while(1)
32
    {
33
        nbytes = read(s, &frame, sizeof(struct can_frame));
34
35
        printf("vcan1: ");
36
        fflush(stdout);
37
38
        if (nbytes < 0) {
39
            perror("Read");
40
            return 1;
41
        }
42
43
        printf("0x%03X [%d] ",frame.can_id, frame.can_dlc);
44
        fflush(stdout);
45
46
        for (i = 0; i < frame.can_dlc; i++)
47
        {
48
            printf("%02X ",frame.data[i]);
49
            fflush(stdout);
50
        }
51
52
53
    }
54
55
    printf("\r\n");
56
57
    if (close(s) < 0) {
58
        perror("Close");
59
        return 1;
60
    }
61
62
    return 0;
63
}

bis auf die Zeile char *ifname = "vcan0"; sehen diese alle gleich aus.

jetzt erstelle ich im Terminal vcan0 bis vcan4
Wenn ich jetzt eine Nachricht schicke, kommt diese auch im 
Interrupthandler an. Allerdings in allen.
Wo kann/muss ich es genau einstellen, dass Nachrichten von vcan0 nur in 
pVCan1Rx ankmomen?

von Imonbln (Gast)


Lesenswert?

Johannes schrieb:
> bis auf die Zeile char *ifname = "vcan0"; sehen diese alle gleich aus.

Für sowas hat pthread_create hinten das "void *arg" wenn die Threads 
identisch sind, dann schreibe den Code einmal und denke dir eine schöne 
Struktur aus um die Unterschiede per Parameter zu übergeben.

Johannes schrieb:
> jetzt erstelle ich im Terminal vcan0 bis vcan4
> Wenn ich jetzt eine Nachricht schicke, kommt diese auch im
> Interrupthandler an. Allerdings in allen.

Glückwunsch 50% Erfolg, aber mal ernsthaft, hast du den Return des ioctl 
mal überprüft?
1
ioctl(s, SIOCGIFINDEX, &ifr);

Wenn der schiefgeht ist, der Rest des Threads undefiniert, was zu dein 
beschrieben verhalten führen kann.
1
strcpy(ifr.ifr_name, ifname );
 das ist sicher ein Security-Issue, die, wenn ifname hinreichend lang 
ist gibt es ein Bufferoverflow, wie viele Bytes verträgt denn 
ifr.ifr_name?

der Teil ist Toter Code und kann nicht erreicht werden, da es nur ein 
return 1 die while(1) Schleife verlassen kann!
1
printf("\r\n");
2
3
if (close(s) < 0) {
4
perror("Close");
5
return 1;
6
}
7
8
return 0;

deine Funktionssignatur ist:
1
void * pVCan1Rx(__attribute__((unused)) void * pData);

Du gibst aber Int werte zurück, diese werden implizit auf Pointer 
gecastet, ein Pointer auf die Adresse 1 (return 1) ist aber Käse und 
führt im besten fall nur zu einem Coredump wenn jemand den Return wert 
weiter verwendet. Deine Fehlerbehandlung durch die Return Values ist 
daher komplett für die Tonne und sollte dringend überarbeitet werden.

Johannes schrieb:
> Wo kann/muss ich es genau einstellen, dass Nachrichten von vcan0 nur in
> pVCan1Rx ankmomen?

Es war schwer denn Code zu schreiben, also muss es schwer sein Ihn zu 
lesen!
Das ist für 3. eine fiese Falle, wenn du wirklich für jeden can eine 
eigene Funktion implementierst muss die für vcan0 auch pVCan0Rx heißen. 
C ist zerobased und alles andere sorgt nur für Verwirrung und bei 
hinreichend Komplexen Programmen dafür das die Programmierer falsche 
annahmen machen.

und Zuletzt, wenn du das Gefühl hast nach jeden Printf stdout zu 
flushen, solltest du Nachdenke ob printf dein Freund ist und/oder du mit 
tcsetattr() das Terminal nicht vielleicht besser auf unbufferd umstellen 
willst.

von Rolf M. (rmagnus)


Lesenswert?

Johannes schrieb:
> bis auf die Zeile char *ifname = "vcan0"; sehen diese alle gleich aus.

Dann würde ich eine Funktion machen und ihr den ifname als Paramter 
übergeben. Woebei man hier nicht unbedingt vier Threads braucht. Du 
kannst auch alle vier CANs in einem Thread und mit einer Funktion lesen, 
wenn du select() oder poll() nutzt.

> Wenn ich jetzt eine Nachricht schicke, kommt diese auch im
> Interrupthandler an. Allerdings in allen.

Ich verstehe nicht, was du mit "Interrupthandler" meinst. Interrupts 
kommen in deinem Code überhaupt nicht vor.

von Imonbln (Gast)


Lesenswert?

Rolf M. schrieb:
> alle vier CANs in einem Thread und mit einer Funktion lesen,
> wenn du select() oder poll() nutzt.

Theoretisch nicht mal select oder poll aus 
https://www.kernel.org/doc/Documentation/networking/can.txt

  To bind a socket to all(!) CAN interfaces the interface index must
  be 0 (zero). In this case the socket receives CAN frames from every
  enabled CAN interface. To determine the originating CAN interface
  the system call recvfrom(2) may be used instead of read(2). To send
  on a socket that is bound to 'any' interface sendto(2) is needed to
  specify the outgoing interface.

Und danach dann mit recvfrom die Nachrichten lesen und fröhlich 
ausgeben. Aber ich glaube dann lernt der TO weniger, er hat sich halt 
für diesen Overengineering Ansatz entschieden und nun sollte er mal 
schön die Geister bändigen, die er gerufen hat.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.