/* * Copyright (c) 2013 EIA Electronics * * Authors: * Kurt Van Dijck * * This program is free software; you can redistribute it and/or modify * it under the terms of the version 2 of the GNU General Public License * as published by the Free Software Foundation */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libj1939.h" static const char help_msg[] = "testj1939: demonstrate j1939 use\n" "Usage: testj1939 FROM TO\n" " FROM / TO - or [IFACE][:[SA][,[PGN][,NAME]]]\n" "Options:\n" " -v Print relevant API calls\n" " -s[=LEN] Initial send of LEN bytes dummy data\n" " -r Receive (and print) data\n" " -e Echo incoming packets back\n" " This atually receives packets\n" " -c Issue connect()\n" " -p=PRIO Set priority to PRIO\n" " -n Emit 64bit NAMEs in output\n" " -w[TIME] Return after TIME (default 1) seconds\n" "\n" "Example:\n" "testj1939 can1 20\n" "\n" ; static const char optstring[] = "?vs::rep:cnw::"; static void parse_canaddr(char *spec, struct sockaddr_can *paddr) { char *str; str = strsep(&spec, ":"); if (strlen(str)) paddr->can_ifindex = if_nametoindex(str); str = strsep(&spec, ","); if (str && strlen(str)) paddr->can_addr.j1939.addr = strtoul(str, NULL, 0); str = strsep(&spec, ","); if (str && strlen(str)) paddr->can_addr.j1939.pgn = strtoul(str, NULL, 0); str = strsep(&spec, ","); if (str && strlen(str)) paddr->can_addr.j1939.name = strtoul(str, NULL, 0); } static void onsigalrm(int sig) { error(0, 0, "exit as requested"); exit(0); } static void schedule_oneshot_itimer(double delay) { struct itimerval it = {}; it.it_value.tv_sec = delay; it.it_value.tv_usec = (long)(delay * 1e6) % 1000000; if (setitimer(ITIMER_REAL, &it, NULL) < 0) error(1, errno, "schedule itimer %.3lfs", delay); } /* main */ int main(int argc, char *argv[]) { int ret, sock, opt, j; int verbose = 0; socklen_t peernamelen; struct sockaddr_can sockname = { .can_family = AF_CAN, .can_addr.j1939 = { .addr = J1939_NO_ADDR, .name = J1939_NO_NAME, .pgn = J1939_NO_PGN, }, }, peername = { .can_family = AF_CAN, .can_addr.j1939 = { .addr = J1939_NO_ADDR, .name = J1939_NO_NAME, .pgn = J1939_NO_PGN, }, }; uint8_t dat[128]; int valid_peername = 0; int todo_send = 0, todo_recv = 0, todo_echo = 0, todo_prio = -1; int todo_connect = 0, todo_names = 0, todo_wait = 0; /* argument parsing */ while ((opt = getopt(argc, argv, optstring)) != -1) switch (opt) { case 'v': verbose = 1; break; case 's': todo_send = strtoul(optarg ?: "8", NULL, 0); break; case 'r': todo_recv = 1; break; case 'e': todo_echo = 1; break; case 'p': todo_prio = strtoul(optarg, NULL, 0); break; case 'c': todo_connect = 1; break; case 'n': todo_names = 1; break; case 'w': schedule_oneshot_itimer(strtod(optarg ?: "1", NULL)); signal(SIGALRM, onsigalrm); todo_wait = 1; break; default: fputs(help_msg, stderr); exit(1); break; } if (argv[optind]) { if (strcmp("-", argv[optind])) parse_canaddr(argv[optind], &sockname); ++optind; } if (argv[optind]) { if (strcmp("-", argv[optind])) { parse_canaddr(argv[optind], &peername); valid_peername = 1; } ++optind; } /* open socket */ if (verbose) fprintf(stderr, "- socket(PF_CAN, SOCK_DGRAM, CAN_J1939);\n"); sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); if (ret < 0) error(1, errno, "socket(j1939)"); if (todo_prio >= 0) { if (verbose) fprintf(stderr, "- setsockopt(, SOL_CAN_J1939, SO_J1939_SEND_PRIO, &%i);\n", todo_prio); ret = setsockopt(sock, SOL_CAN_J1939, SO_J1939_SEND_PRIO, &todo_prio, sizeof(todo_prio)); if (ret < 0) error(1, errno, "set priority %i", todo_prio); } if (verbose) fprintf(stderr, "- bind(, %s, %zi);\n", libj1939_addr2str(&sockname), sizeof(sockname)); ret = bind(sock, (void *)&sockname, sizeof(sockname)); if (ret < 0) error(1, errno, "bind()"); if (todo_connect) { if (!valid_peername) error(1, 0, "no peername supplied"); if (verbose) fprintf(stderr, "- connect(, %s, %zi);\n", libj1939_addr2str(&peername), sizeof(peername)); ret = connect(sock, (void *)&peername, sizeof(peername)); if (ret < 0) error(1, errno, "connect()"); } if (todo_send) { /* initialize test vector */ for (j = 0; j < sizeof(dat); ++j) dat[j] = ((2*j) << 4) + ((2*j+1) & 0xf); /* send data */ /* * when using connect, do not provide additional * destination information and use send() */ if (valid_peername && !todo_connect) { if (verbose) fprintf(stderr, "- sendto(, , %i, 0, %s, %zi);\n", todo_send, libj1939_addr2str(&peername), sizeof(peername)); ret = sendto(sock, dat, todo_send, 0, (void *)&peername, sizeof(peername)); } else { /* * we may do sendto(sock, dat, todo_send, 0, NULL, 0) * as well, but using send() demonstrates the API better */ if (verbose) fprintf(stderr, "- send(, , %i, 0);\n", todo_send); ret = send(sock, dat, todo_send, 0); } if (ret < 0) error(1, errno, "sendto"); } /* main loop */ if ((todo_echo || todo_recv) && verbose) fprintf(stderr, "- while (1)\n"); while (todo_echo || todo_recv) { /* * re-use peername for storing the sender's peername of * received packets */ if (verbose) fprintf(stderr, "- recvfrom(, , %zi, 0, &, %zi);\n", sizeof(peername), sizeof(peername)); peernamelen = sizeof(peername); ret = recvfrom(sock, dat, sizeof(dat), 0, (void *)&peername, &peernamelen); if (ret < 0) { if (EINTR == errno) { if (verbose) fprintf(stderr, "-\t\n"); continue; } error(1, errno, "recvfrom()"); } if (todo_echo) { if (verbose) fprintf(stderr, "- sendto(, , %i, 0, %s, %i);\n", ret, libj1939_addr2str(&peername), peernamelen); ret = sendto(sock, dat, ret, 0, (void *)&peername, peernamelen); if (ret < 0) error(1, errno, "sendto"); } if (todo_recv) { if (todo_names && peername.can_addr.j1939.name) printf("%016llx ", peername.can_addr.j1939.name); printf("%02x %05x:", peername.can_addr.j1939.addr, peername.can_addr.j1939.pgn); for (j = 0; j < ret; ++j) printf(" %02x", dat[j]); printf("\n"); } } if (todo_wait) for (;;) sleep(1); return 0; }