328 lines
7.0 KiB
C
328 lines
7.0 KiB
C
/*
|
|
* Copyright 2018, Digi International Inc.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <libgen.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <libdigiapix/can.h>
|
|
|
|
#define TX_RETRIES 10
|
|
|
|
#define RANDOM_ID_BIT 0
|
|
#define EXT_ID_BIT 1
|
|
#define RTR_BIT 2
|
|
#define RANDOM_DLC_BIT 3
|
|
|
|
#define RANDOM_ID_MASK 0x01
|
|
#define EXT_ID_MASK 0x02
|
|
#define RTR_BIT_MASK 0x04
|
|
#define RANDOM_DLC_MASK 0x08
|
|
|
|
#define RANDOM_ID(random_id) (random_id << RANDOM_ID_BIT)
|
|
#define EXT_ID(extended_id) (extended_id << EXT_ID_BIT)
|
|
#define RTR(rtr) (rtr << RTR_BIT)
|
|
#define RANDOM_DLC(dlc) (dlc << RANDOM_DLC_BIT)
|
|
|
|
static can_if_t *can_if;
|
|
static bool running = true;
|
|
|
|
/*
|
|
* usage_and_exit() - Show usage information and exit with 'exitval' return
|
|
* value
|
|
*
|
|
* @name: Application name.
|
|
* @exitval: The exit code.
|
|
*/
|
|
static void usage_and_exit(char *name, int exitval)
|
|
{
|
|
printf(
|
|
"Example application using libdigiapix CAN support\n"
|
|
"\n"
|
|
"Usage: %s -i <can-iface> -b <bitrate> [options]\n\n"
|
|
"-i <can-iface> Name of the CAN interface\n"
|
|
"-b <bitrate> Bitrate to use (Hz)\n"
|
|
"-s <sample-point> CAN bitrate sample point\n"
|
|
"-n <num_msgs> Number of messages to send (default 1)\n"
|
|
"-t <delay> Inter frame delay in ms (default 100)\n"
|
|
"-I <msg_id> Message id in hex (default 123)\n"
|
|
"-l <data_length> Payload length (default 8)\n"
|
|
"-o Enable CAN FD support\n"
|
|
"--- CAN FD options ---\n"
|
|
" -d <dbitrate> Maximum data bitrate for CAN FD (Hz)\n"
|
|
" -p <dsample-point> CAN FD data bitate sample point\n"
|
|
"---\n"
|
|
"-r Generate a random ID (will ignore the -I parameter)\n"
|
|
"-c Generate a random payload (will ignore the -l parameter)\n"
|
|
"-e Use extended id\n"
|
|
"-R Set RTR\n"
|
|
"\n"
|
|
"Examples:\n"
|
|
"%s -i can0 -b 500000 -n 100 -R\n"
|
|
"%s -i can1 -b 100000\n"
|
|
"%s -i can1 -b 100000 -d 100000 -n 10 -o\n"
|
|
"\n", name, name, name, name);
|
|
|
|
exit(exitval);
|
|
}
|
|
|
|
/*
|
|
* ms_sleep() - Wait ms miliseconds
|
|
*
|
|
* ms: Number of ms to wait.
|
|
*/
|
|
void ms_sleep(uint32_t ms)
|
|
{
|
|
struct timespec ts;
|
|
|
|
if (ms < 1000) {
|
|
ts.tv_sec = 0;
|
|
ts.tv_nsec = ms * 1000000;
|
|
} else {
|
|
ts.tv_sec = ms / 1000;
|
|
ts.tv_nsec = (ms % 1000) * 1000000;
|
|
}
|
|
|
|
nanosleep(&ts, NULL);
|
|
}
|
|
|
|
/*
|
|
* cleanup() - Frees all the allocated memory before exiting
|
|
*/
|
|
static void cleanup(void)
|
|
{
|
|
if (can_if) {
|
|
ldx_can_free(can_if);
|
|
running = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* sigaction_handler() - Handler to execute after receiving a signal
|
|
*
|
|
* @signum: Received signal.
|
|
*/
|
|
static void sigaction_handler(int signum)
|
|
{
|
|
/* 'atexit' executes the cleanup function */
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/*
|
|
* register_signals() - Registers program signals
|
|
*/
|
|
static void register_signals(void)
|
|
{
|
|
struct sigaction action;
|
|
|
|
action.sa_handler = sigaction_handler;
|
|
action.sa_flags = 0;
|
|
sigemptyset(&action.sa_mask);
|
|
|
|
sigaction(SIGHUP, &action, NULL);
|
|
sigaction(SIGINT, &action, NULL);
|
|
sigaction(SIGTERM, &action, NULL);
|
|
}
|
|
|
|
void update_msg(struct canfd_frame *frame, uint32_t id, uint8_t dlc, uint8_t flags)
|
|
{
|
|
uint8_t index;
|
|
|
|
if (flags & RANDOM_ID_MASK)
|
|
id = rand() % 2047 + 1;
|
|
|
|
if (flags & EXT_ID_MASK) {
|
|
frame->can_id = id & CAN_EFF_MASK;
|
|
frame->can_id |= CAN_EFF_FLAG;
|
|
} else {
|
|
frame->can_id = id & CAN_SFF_MASK;
|
|
}
|
|
|
|
if (flags & RTR_BIT_MASK)
|
|
frame->can_id |= CAN_RTR_FLAG;
|
|
|
|
if (flags & RANDOM_DLC_MASK)
|
|
dlc = rand() % 8 + 1;
|
|
|
|
frame->len = dlc;
|
|
|
|
/* Currently we just do incremental updates on the payload */
|
|
for (index = 0; index < dlc; index++) {
|
|
frame->data[index] = frame->data[index] + 1;
|
|
if (frame->data[index])
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char *name = basename(argv[0]);
|
|
char *iface = NULL;
|
|
can_if_cfg_t ifcfg;
|
|
int opt;
|
|
int ret;
|
|
uint32_t ms_delay = 1;
|
|
uint32_t num_msgs = 1;
|
|
uint32_t msg_id = 0x123;
|
|
uint8_t msg_len = 8;
|
|
uint8_t flags = 0;
|
|
float sp = 0.0;
|
|
struct canfd_frame frame;
|
|
|
|
srand (time(NULL));
|
|
|
|
if (argc <= 3) {
|
|
usage_and_exit(name, EXIT_FAILURE);
|
|
}
|
|
|
|
ldx_can_set_defconfig(&ifcfg);
|
|
|
|
while ((opt = getopt(argc, argv, "i:b:n:t:I:l:a:d:s:t:oerRc")) > 0) {
|
|
switch (opt) {
|
|
case 'i':
|
|
iface = optarg;
|
|
break;
|
|
|
|
case 'b':
|
|
ifcfg.bitrate = strtoul(optarg, NULL, 10);
|
|
break;
|
|
|
|
case 'd':
|
|
ifcfg.dbitrate = strtoul(optarg, NULL, 10);
|
|
break;
|
|
|
|
case 'n':
|
|
num_msgs = strtoul(optarg, NULL, 10);
|
|
break;
|
|
|
|
case 't':
|
|
ms_delay = strtoul(optarg, NULL, 10);
|
|
break;
|
|
|
|
case 's':
|
|
sp = strtof(optarg, NULL);
|
|
ifcfg.bit_timing.sample_point = (__u32)(sp * 1000);
|
|
break;
|
|
|
|
case 'a':
|
|
sp = strtof(optarg, NULL);
|
|
ifcfg.dbit_timing.sample_point = (__u32)(sp * 1000);
|
|
break;
|
|
|
|
case 'I':
|
|
msg_id = strtoul(optarg, NULL, 16);
|
|
break;
|
|
|
|
case 'l':
|
|
msg_len = strtoul(optarg, NULL, 10);
|
|
break;
|
|
|
|
case 'e':
|
|
flags |= EXT_ID_MASK;
|
|
break;
|
|
|
|
case 'o':
|
|
ifcfg.canfd_enabled = true;
|
|
break;
|
|
|
|
case 'r':
|
|
flags |= RANDOM_ID_MASK;
|
|
break;
|
|
|
|
case 'R':
|
|
flags |= RTR_BIT_MASK;
|
|
break;
|
|
|
|
case 'c':
|
|
flags |= RANDOM_DLC_MASK;
|
|
break;
|
|
|
|
default:
|
|
usage_and_exit(name, EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if (!iface) {
|
|
fprintf(stderr, "Error: CAN interface not specified\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
printf("Requesting CAN interface %s... ", iface);
|
|
|
|
can_if = ldx_can_request_by_name(iface);
|
|
if (!can_if) {
|
|
printf("ERROR\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
printf("OK\n");
|
|
|
|
/* Register signals and exit cleanup function */
|
|
atexit(cleanup);
|
|
register_signals();
|
|
|
|
printf("Initializing CAN interface... ");
|
|
ret = ldx_can_init(can_if, &ifcfg);
|
|
if (ret) {
|
|
printf("ERROR\n");
|
|
goto error;
|
|
}
|
|
printf("OK\n");
|
|
|
|
memset(&frame, 0, sizeof(frame));
|
|
|
|
while (running && num_msgs) {
|
|
int retries = TX_RETRIES;
|
|
/* If we need to create more configuration bits, we have this variable flags*/
|
|
update_msg(&frame, msg_id, msg_len, flags);
|
|
|
|
while (retries--) {
|
|
ret = ldx_can_tx_frame(can_if, &frame);
|
|
if (!ret) {
|
|
break;
|
|
} else if (ret == -CAN_ERROR_TX_RETRY_LATER) {
|
|
ms_sleep(1);
|
|
} else {
|
|
printf("Failed to send CAN frame (%d)\n", ret);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (!retries) {
|
|
printf("Failed to send CAN frame after %d tries\n", TX_RETRIES);
|
|
goto error;
|
|
}
|
|
|
|
num_msgs--;
|
|
|
|
if (ms_delay)
|
|
ms_sleep(ms_delay);
|
|
}
|
|
|
|
ms_sleep(1000);
|
|
|
|
error:
|
|
|
|
printf("\n\nCan send frame application has finished\n");
|
|
|
|
return ret;
|
|
}
|