linux-can / can-utils

Linux-CAN / SocketCAN user space applications
2.39k stars 711 forks source link

Dont recive ETP and TP messages. #228

Closed HenriqueRicardoFigueira closed 4 years ago

HenriqueRicardoFigueira commented 4 years ago

hi, I am testing the J1939 and I cannot receive messages from the transport protocol, however when I use candump the messages are arriving normally. I don't know if this is the right place for this kind of doubt, otherwise I apologize. code to receive message

void threadRecepcao(int sock) {
    static socklen_t peernamelen;
    static sockaddr_can saddr;
    std::array<char, 16> data;
    peernamelen = sizeof(saddr);
    static int ret;
    static msg msgRec;
    msghdr nozes;
    while (1) {

        ret = recvfrom(sock, &data, 16, 0, (struct sockaddr*) &saddr,
                &peernamelen);
        if (ret == -1) {
            cout << ">> Message receive error!" << "\n";
        } else {
            cout << "pgn > " << hex << saddr.can_addr.j1939.pgn << " \n";

        }
        msgRec.byte = data;
        msgRec.addr = saddr;
        bufferMsg.push_front(msgRec);
    }
}
marckleinebudde commented 4 years ago

How do you create the socket? Have a look at https://github.com/linux-can/can-utils/blob/master/j1939cat.c for a j1939 example.

HenriqueRicardoFigueira commented 4 years ago

Good evening marckleinebudde, thanks for your attention. I create the socket like this:

int main() {

    Iso iso;
    int sock, ret;
    msg msgT;
    //nova váriavel para resolver problemas de tipo
    mensage msgIso;
    sockaddr_can sockname, saddr;

    memset(&saddr, 0, sizeof(saddr));
    memset(&sockname, 0, sizeof(sockname));

    sock = socket(PF_CAN, SOCK_DGRAM, CAN_J1939);
    if (sock == -1) {
        cout << ">> ERRO ao criar o socket.\n";
        exit(1);
    }

    sockname.can_family = AF_CAN;
    sockname.can_addr.j1939.addr = 0x86; //J1939_IDLE_ADDR;
    sockname.can_addr.j1939.name = 0xA0088100FBC007DEULL;
    sockname.can_addr.j1939.pgn = J1939_NO_PGN;
    sockname.can_ifindex = if_nametoindex("can0");

    ret = bind(sock, (struct sockaddr*) &sockname, sizeof(sockname));
    if (ret == -1) {
        cout << ">> ERRO ao vincular soket ao perifério da CAN; (bind)" << "\n";
        exit(1);
    }

    int value = 1;
    setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &value, sizeof(value));
    struct j1939_filter filt[5] = {};
    filt[0].pgn = 0xC800;
    filt[0].pgn_mask = 0x3fff;
    filt[1].pgn = 0xE600;
    filt[1].pgn_mask = 0x3fff;
    filt[1].pgn = 0xEC00;
    filt[1].pgn_mask = 0x3fff;
    filt[2].pgn = J1939_PGN_ADDRESS_CLAIMED;
    filt[2].pgn_mask =  J1939_PGN_PDU1_MAX;
    filt[3].pgn = J1939_PGN_REQUEST;
    filt[3].pgn_mask = J1939_PGN_PDU1_MAX;
    filt[4].pgn = J1939_PGN_ADDRESS_COMMANDED;
    filt[4].pgn_mask = J1939_PGN_MAX;

    ret = setsockopt(sock, SOL_CAN_J1939, SO_J1939_FILTER, &filt, sizeof(filt));
    if (ret == -1) {
        cout << ">> ERRO ao atribuir filtros" << "\n";
        exit(1);
    }

    uint64_t nome = htole64(sockname.can_addr.j1939.name);

    saddr.can_family = AF_CAN;
    saddr.can_addr.j1939.pgn = J1939_PGN_ADDRESS_CLAIMED;
    saddr.can_addr.j1939.addr = J1939_NO_ADDR;

    sendto(sock, &nome, sizeof(nome), 0, (const struct sockaddr*) &saddr,
            sizeof(saddr));

    saddr.can_addr.j1939.addr = 0;

    thread rec(threadRecepcao, sock);
    thread t50ms(thread50ms, &id50ms);

    const unsigned char dados[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    while (1) {
        if (iso.flagEtp == 0)
            iso.sendMessage(sock);

        if (iso.getSendOb() == 1) {
            iso.sendOb(sock);
        }
        if (id1s == 1) {
            id1s = 0;
        }
        if (id500ms) {
            id500ms = 0;

        }
        if (id250ms == 1) {
            id250ms = 0;

        }
        if (id100ms == 1) {
            id100ms = 0;

        }
        if (id50ms) {
            id50ms = 0;

        }

        if (!bufferMsg.empty()) {
            msgT = bufferMsg.back();
            bufferMsg.pop_back();
            if (msgT.addr.can_addr.j1939.pgn == 0xe700) {
                //printf(" > req: \n");
                //fPrintMsg(msgT);
            } else if (msgT.addr.can_addr.j1939.pgn == 0xe600) {
                //fPrintMsg(msgT);
                msgIso.addr = msgT.addr;
                msgIso.byte = msgT.byte;
                iso.trataE600(msgIso, sock);
            } else if (msgT.addr.can_addr.j1939.pgn == 0xC800) {
                printf("msg C800 ");
                iso.print(msgT.byte);
            }
            if (msgT.addr.can_addr.j1939.pgn == 0xea00) {
                printf("msg ea00 ");
                fPrintMsg(msgT);
            }
        }

    }

    return 0;
}

I receive all messages as normal. However when the png is C8 or EC and the recipient is me, I don't get it. I only get paid when they go to broadcast. When I use candump, messages arrive normally.

olerem commented 4 years ago

Hi @HenriqueRicardoFigueira ,

you filter setting looks incorrect:

    filt[0].pgn = 0xC800;
    filt[0].pgn_mask = 0x3fff;

0xC800 & 0x3fff = 0x800 And since you are configuring PDU1 PGN, it makes no sense to apply mask on first two bytes. Instead, you should use J1939_PGN_PDU1_MAX (0x3ff00)

Then, after J1939_PGN_ADDRESS_CLAIMED message, only after 250ms the kernel will start to forward message to the socket. Otherwise ECU address claiming process is not completed.

olerem commented 4 years ago

One more issue, it is better to no mix as socket for address claiming, which needs broadcast support. And other sockets where broadcast is not needed.

HenriqueRicardoFigueira commented 4 years ago

Hi @olerem, thanks for the attention. I made the changes to the filters and I still don't receive ETP messages. Any other PGN the socket receives and address confirmation message.

filt[0].pgn = 0xc800;
filt[0].pgn_mask =  J1939_PGN_PDU1_MAX;
filt[1].pgn = J1939_PGN_REQUEST;
filt[1].pgn_mask = J1939_PGN_PDU1_MAX;
filt[2].pgn = J1939_PGN_ADDRESS_COMMANDED;
filt[2].pgn_mask = J1939_PGN_MAX;
filt[3].pgn = 0xE600;
filt[3].pgn_mask = J1939_PGN_MAX;
filt[4].pgn = J1939_PGN_ADDRESS_CLAIMED;
filt[4].pgn_mask = J1939_PGN_PDU1_MAX;

Regarding the socket for address claiming, I didn't quite understand. Can I have multiple sockets for the same address?

olerem commented 4 years ago

Hi @olerem, thanks for the attention. I made the changes to the filters and I still don't receive ETP messages. Any other PGN the socket receives and address confirmation message.

Heh... silly me, i didn't noticed that you are trying to filter (E)TP CTRL messages.

No, you will not get them in user space. The xTP are assembled in the kernel. The user-space will get assembled data. This PGNs should never go to the user space (except by using CAN_RAW socket):

#define J1939_ETP_PGN_CTL 0xc800                                                                                         
#define J1939_ETP_PGN_DAT 0xc700                                                                                         
#define J1939_TP_PGN_CTL 0xec00                                                                                          
#define J1939_TP_PGN_DAT 0xeb00

Regarding the socket for address claiming, I didn't quite understand. Can I have multiple sockets for the same address?

Yes, you can have multiple sockets for the same address. You are even encouraged to do so. In fact, you can use existing j1939acd (adress claiming daemon), see: https://github.com/linux-can/can-utils/blob/master/j1939acd.c

If you are using multiple sockets or applications for same NAME:

The kernel j1939 stack is designed to allow to use simplest programming model. Even using read()/write() calls should be enough for working application.

HenriqueRicardoFigueira commented 4 years ago

@olerem so how do i get these messages? because to transfer the objectpool by ETP I need to receive the reply messages from the VT.

When I try to send a message through sendto() with PGN 0xc800 the socket returns an error 33. Is it for the same reason? I can only send messages to PGN 0xc800 with cansend.

HenriqueRicardoFigueira commented 4 years ago

Thanks for the clarifications, I was lost for a while kkkk it was bad if some questions are too silly, I'm starting now.

olerem commented 4 years ago

@olerem so how do i get these messages? because to transfer the objectpool by ETP I need to receive the reply messages from the VT.

When I try to send a message through sendto() with PGN 0xc800 the socket returns an error 33. Is it for the same reason? I can only send messages to PGN 0xc800 with cansend.

You do not need to send TP control messages. The kernel will send it for you. You need to send only payload/data. See: https://github.com/linux-can/can-utils/blob/master/j1939cat.c It is an example for sending and receiving messages using (E)TP.

HenriqueRicardoFigueira commented 4 years ago

Hi @olerem tanks for your help. I was able to send ob through the send () function. Some doubts: 1 - For ETP sending, do I need to make several send () calls passing the parsed objectpool? 2- I still cannot receive the EC or C8 control messages.

void recive_msgs(int sock) {
    std::array<char, 16> data;
    int ret;
    void *buf;
        buf = &data;
    static msg msgRec;
    while (1) {
        ret = recv(sock, buf, sizeof(data), 0);
        if (ret < 0) {
            cout << ">> Erro recive msg" << "\n";
            printf("%i \n", errno);
            printf("%s \n", strerror(errno));
                        exit(1);
        } else {
            msgRec.byte = data;
            bufferMsg.push_front(msgRec);
        }
    }
}
marckleinebudde commented 4 years ago

1 - For ETP sending, do I need to make several send () calls passing the parsed objectpool?

One send() will result in one (E)TP message with the size of the length argument of the send() call.

Note: send() will return the number of send bytes, that might be less than you specified with the initial send() call. You have to call send() again, but this time send the remaining data. This means you have to increase the buf pointer by the number of already send bytes and decrease the length accordingly.

Once all data of the initial send() is actually send, you can start a new send() call, which will result in a new (E)TP message.

2- I still cannot receive the EC or C8 control messages.

The kernel will receive the (E)TP control messages for your and handle them. What do you want to do with them?

HenriqueRicardoFigueira commented 4 years ago

Hi @marckleinebudde thanks for the help, so i wanted to receive confirmation e-mail from vt (EoMA).

I understand, so the send does not send the total on the first call? Sorry for the beginner's questions, things have started to become clear now. Thank you for your help.

marckleinebudde commented 4 years ago

send() always tries to send data. How much it actually sent is indicated by the return value.

Keep in mind, the length indicated by the first send() is the size of the (E)TP message. You have to complete that (E)TP message first (with subsequent send() calls) before a new (E)TP message can be started.

If you want confirmation about EoMA, have a look at the https://github.com/linux-can/can-utils/blob/master/j1939cat.c - @olerem can probably elaborate on that.

olerem commented 4 years ago

Seek for SCM_TSTAMP_ACK in j1939can.c You will need to subscribe: https://github.com/linux-can/can-utils/blob/master/j1939cat.c#L552 to receive error messages: https://github.com/linux-can/can-utils/blob/master/j1939cat.c#L182

SCM_TSTAMP_ACK - messages was completely transferred (confirmed by EoMA) SCM_TSTAMP_SCHED - we got CTS message and will start transmission. J1939_EE_INFO_TX_ABORT - message transfer was aborted

This kernel code will send this ^ notifications to the user space: https://elixir.bootlin.com/linux/latest/source/net/can/j1939/socket.c#L918

All notifications have a timestamp and a message cookie/tskey. The cookie/tskey is needed to identify which notification belongs to which message/payload.

HenriqueRicardoFigueira commented 4 years ago

@marckleinebudde @olerem I understand now, thanks for the answers. I thought I would receive these control messages as normal messages.

So I think my problem is another one, because the return of send () is the total number of bytes of the objectpool, however it returns the error 131 state not recoverable.

olerem commented 4 years ago

can you please provide candump of you transmission? you will need to run: candump -l can0 if can0 is you main interface

olerem commented 4 years ago

@marckleinebudde @olerem I understand now, thanks for the answers. I thought I would receive these control messages as normal messages.

This would make no sense, since this socket interface allows you to queue multiple messages, you will not be able to map CTS, EoMA to which message it actually belong. And by implementing this socket interface we need to stay as close a possible to existing POSIX model. This is the reason, why you get more or less standard posix messages, instead of J1939/IsoBUS abort messages.

HenriqueRicardoFigueira commented 4 years ago

hi @olerem my addrs is 96. candump-2020-07-30_175245.log

HenriqueRicardoFigueira commented 4 years ago

This would make no sense, since this socket interface allows you to queue multiple messages, you will not be able to map CTS, EoMA to which message it actually belong. And by implementing this socket interface we need to stay as close a possible to existing POSIX model. This is the reason, why you get more or less standard posix messages, instead of J1939/IsoBUS abort messages.

I understand, it really wouldn't make any sense.

olerem commented 4 years ago

Ok, i see. We have properly working start of transfer, but the session is aborted by transmitter:

(1596131577.251266) can0 00C72696#3D27000000E80382
(1596131577.251838) can0 00C72696#3E014D0183019B02
(1596131577.252411) can0 00C72696#3F00850115C80000
(1596131577.253000) can0 00C72696#40FFFFFFFFFFFFFF
// 96 -> 26 ABORT, reason 05
(1596131577.253694) can0 00C82696#FF05FFFFFF00E700
// 26 -> 96 CTS
(1596131577.254265) can0 00C89626#154001010000E700
(1596131577.320444) can0 18E7FF82#23010001FFFFFFFF
(1596131577.341504) can0 1CE6FF26#FE816400960000FF
(1596131577.420243) can0 18E7FF82#23010001FFFFFFFF

Since abort was triggered really fast, I would assume, you stopped to feed the socket with the data. In this case the kernel log would show some errors. Can you please provide kernel log/dmesg.

marckleinebudde commented 4 years ago

Good morning @HenriqueRicardoFigueira,

can you create a log with strace[1] and candump at the same time and send the logs?

[1] strace -tt -o strace.log -- /path/to/your/app APP_ARGS

marckleinebudde commented 4 years ago

We analysed the candump a bit further, and the ETP is aborted after 4 transfers 0x40 messages which is 1792 bytes. @olerem will try to reproduce here, with the kernel stack which limits the number of messages to 0x40.

olerem commented 4 years ago

I can reproduce it

HenriqueRicardoFigueira commented 4 years ago

can you create a log with strace[1] and candump at the same time and send the logs?

Good morning @marckleinebudde , of course, I will do it now. thanks for the help.

I can reproduce it

thanks for the help.

marckleinebudde commented 4 years ago

can you create a log with strace[1] and candump at the same time and send the logs?

Of course, I will do it now.

As @olerem can reproduce the issue now, there's no need for a strace log. Thanks

HenriqueRicardoFigueira commented 4 years ago

can you create a log with strace[1] and candump at the same time and send the logs?

Of course, I will do it now.

As @olerem can reproduce the issue now, there's no need for a strace log. Thanks

Ok

marckleinebudde commented 4 years ago

@HenriqueRicardoFigueira BTW: What's the VT you're working with?

HenriqueRicardoFigueira commented 4 years ago

@HenriqueRicardoFigueira BTW: What's the VT you're working with?

@marckleinebudde I'm using a simulated network.

HenriqueRicardoFigueira commented 4 years ago

@HenriqueRicardoFigueira BTW: What's the VT you're working with?

@marckleinebudde I'm using a simulated network.

If you want I can give you more details in an email.

marckleinebudde commented 4 years ago

I'm interested. :)

HenriqueRicardoFigueira commented 4 years ago

I'm interested. :)

What is your email?

marckleinebudde commented 4 years ago

Use the one from my github page: https://github.com/marckleinebudde

marckleinebudde commented 4 years ago

@HenriqueRicardoFigueira

@olerem has found the issue. It's due to the limitation of 0x40 frames per RTS/CTS cycle. We hope to have the fix on Monday.

HenriqueRicardoFigueira commented 4 years ago

@HenriqueRicardoFigueira

@olerem has found the issue. It's due to the limitation of 0x40 frames per RTS/CTS cycle. We hope to have the fix on Monday.

thanks for your help. @marckleinebudde @olerem