Oliver, Yet another interesting general purpose tool: jdate It sends out the date/time PGN.
Kurt PS: I know you can't test immediately :-) --- Signed-off-by: Kurt Van Dijck <[email protected]> --- Index: can-utils/Makefile =================================================================== --- can-utils/Makefile (revision 1257) +++ can-utils/Makefile (working copy) @@ -56,7 +56,7 @@ PROGRAMS_ISOTP = isotpdump isotprecv isotpsend isotpsniffer isotptun isotpserver PROGRAMS_CANGW = cangw PROGRAMS_SLCAN = slcan_attach slcand -PROGRAMS_J1939 = jacd jspy jsr +PROGRAMS_J1939 = jacd jspy jsr jdate PROGRAMS = can-calc-bit-timing candump cansniffer cansend canplayer cangen canbusload\ log2long log2asc asc2log\ canlogserver bcmserver\ @@ -107,4 +107,5 @@ jspy: libj1939.a jsr: libj1939.a +jdate: libj1939.a Index: can-utils/GNUmakefile.am =================================================================== --- can-utils/GNUmakefile.am (revision 1257) +++ can-utils/GNUmakefile.am (working copy) @@ -56,11 +56,13 @@ bin_PROGRAMS += \ jacd \ jsr \ - jspy + jspy \ + jdate jacd_LDADD = libj1939.la jsr_LDADD = libj1939.la jspy_LDADD = libj1939.la +jdate_LDADD = libj1939.la noinst_LTLIBRARIES += libj1939.la Index: can-utils/jdate.c =================================================================== --- can-utils/jdate.c (revision 0) +++ can-utils/jdate.c (revision 0) @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2011 EIA Electronics + * + * Authors: + * Kurt Van Dijck <[email protected]> + * + * 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 <signal.h> +#include <time.h> +#include <inttypes.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <unistd.h> +#include <getopt.h> +#include <error.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <linux/can.h> +#include <linux/can/j1939.h> +#include "libj1939.h" + +static const char help_msg[] = + "jdate: Send the datetime pgn, each second or on request" "\n" + "Usage: jdate SOURCE" "\n" + " -v, --verbose Show info" "\n" + " -l, --listen Answer to request PGN's only" "\n" + " -i, --iso11783 iso-11783 format" "\n" + "\n" + "Example:" "\n" + "jdate --iso can1:20" "\n" + "\n" + "Specifications" "\n" + " - SAE J1939-71 pgn65254" "\n" + " - ISO 11783-7 Annex B.1" "\n" + "\n" + "Notes" "\n" + "Time/Date Adjust is not yet supported" "\n" + ; + +#ifdef _GNU_SOURCE +static struct option long_opts[] = { + { "help", no_argument, NULL, '?', }, + { "listen", no_argument, NULL, 'l', }, + { "verbose", no_argument, NULL, 'v', }, + { "iso11783", no_argument, NULL, 'i', }, + { }, +}; +#else +#define getopt_long(argc, argv, optstring, longopts, longindex) \ + getopt((argc), (argv), (optstring)) +#endif +static const char optstring[] = "liv?"; + +/* global variables */ +static struct { + const char *source; + int periodic; + int verbose; + int iso; + int sig_term; + int sig_alrm; +} s = { + .source = "can0:", + .periodic = 1, +}; + +/* j1939 socket */ +static const struct j1939_filter filt[] = { + { + .pgn = 0x0ea00, + .pgn_mask = 0x3ff00, + }, +}; + +static int open_socket(const char *source) +{ + int ret, sock; + struct sockaddr_can saddr; + + ret = libj1939_str2addr(source, 0, &saddr); + if (ret < 0) + error(1, 0, "source spec '%s'", source); + if (saddr.can_addr.j1939.pgn != J1939_NO_PGN) + error(1, 0, "no pgn allowed in '%s'", source); + saddr.can_addr.j1939.pgn = 0xfee6; + + sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); + if (ret < 0) + error(1, errno, "socket(j1939)"); + + ret = setsockopt(sock, SOL_CAN_J1939, SO_J1939_FILTER, + &filt, sizeof(filt)); + if (ret < 0) + error(1, errno, "setsockopt filter"); + + ret = bind(sock, (void *)&saddr, sizeof(saddr)); + if (ret < 0) + error(1, errno, "bind()"); + return sock; +} + +/* signa handling */ +static void sighandler(int sig, siginfo_t *info, void *vp) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + s.sig_term = 1; + break; + case SIGALRM: + s.sig_alrm = 1; + break; + } +} + +static void install_signal(int sig) +{ + int ret; + struct sigaction sigact = { + .sa_sigaction = sighandler, + .sa_flags = SA_SIGINFO, + }; + + sigfillset(&sigact.sa_mask); + ret = sigaction(sig, &sigact, NULL); + if (ret < 0) + error(1, errno, "sigaction for signal %i", sig); +} + +static void schedule_repeating_itimer(int msec) +{ + int ret; + struct itimerval val = {}; + + val.it_value.tv_sec = msec / 1000; + val.it_value.tv_usec = (msec % 1000) * 1000; + val.it_interval = val.it_value; + + s.sig_alrm = 0; + do { + ret = setitimer(ITIMER_REAL, &val, NULL); + } while ((ret < 0) && (errno == EINTR)); + if (ret < 0) + error(1, errno, "setitimer %i msec", msec); +} + +static void fill_datetime(uint8_t dat[8]) +{ + struct timeval tv; + struct timezone tz; + struct tm tm; + + if (gettimeofday(&tv, &tz) < 0) + error(1, errno, "gettimeofday"); + + tm = *gmtime(&tv.tv_sec); + dat[0] = tm.tm_sec *4; + dat[1] = tm.tm_min; + dat[2] = tm.tm_hour; + dat[3] = tm.tm_mon +1; + dat[4] = tm.tm_mday *4; + dat[5] = tm.tm_year - 85; + if (s.iso) { + dat[6] = 0; + dat[7] = (-tz.tz_minuteswest / 60) + 24; + } else { + dat[6] = (-tz.tz_minuteswest % 60) + 125; + dat[7] = (-tz.tz_minuteswest / 60) + 125; + } +} + +static int ratelimit_errno(int _errno) +{ + static int saved_errno; + int ret; + + ret = _errno != saved_errno; + saved_errno = _errno; + return ret; +} + +/* main */ +int main(int argc, char *argv[]) +{ + int ret, sock, pgn, opt; + uint8_t dat[9]; + struct sockaddr_can daddr; + +#ifdef _GNU_SOURCE + program_invocation_name = program_invocation_short_name; +#endif + /* argument parsing */ + while ((opt = getopt_long(argc, argv, optstring, long_opts, NULL)) != -1) + switch (opt) { + case 'l': + s.periodic = 0; + break; + case 'i': + s.iso = 1; + break; + case 'v': + ++s.verbose; + break; + default: + fputs(help_msg, stderr); + exit(1); + break; + } + if (argv[optind]) + s.source = argv[optind++]; + /* args done */ + + ret = sock = open_socket(s.source); + + install_signal(SIGTERM); + install_signal(SIGINT); + install_signal(SIGALRM); + install_signal(SIGUSR1); + install_signal(SIGUSR2); + + if (s.periodic) + schedule_repeating_itimer(1000); + + while (!s.sig_term) { + if (s.sig_alrm) { + /* send datetime */ + memset(&daddr, 0, sizeof(daddr)); + + fill_datetime(dat); + ret = send(sock, dat, 8, 0); + if (ret < 0) + switch (errno) { + case EINTR: + continue; + case ENOBUFS: + case EHOSTDOWN: + case EADDRNOTAVAIL: + if (ratelimit_errno(errno)) + error(0, errno, "j1939 send"); + break; + default: + error(1, errno, "j1939 send"); + break; + } else if (ratelimit_errno(0)) + error(0, 0, "j1939 send ok"); + + s.sig_alrm = 0; + if (s.verbose && (ret >= 0)) + error(0, 0, "send"); + } + + ret = recv(sock, dat, sizeof(dat), 0); + if (ret < 0) { + if (EINTR == errno) + continue; + error(1, errno, "recvfrom()"); + } + if (ret < 3) + continue; + pgn = dat[0] + (dat[1] << 8) + ((dat[2] & 0x03) << 16); + + switch (pgn) { + case 0x0fee6: + if (s.verbose) + error(0, 0, "got request"); + s.sig_alrm = 1; + break; + } + } + return 0; +} + Property changes on: can-utils/jdate.c ___________________________________________________________________ Name: svn:eol-style + native _______________________________________________ Socketcan-core mailing list [email protected] https://lists.berlios.de/mailman/listinfo/socketcan-core
