mirror of
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git
synced 2024-11-17 23:13:30 +08:00
8a5179466a
This patch uses the new features of the kernel's netlink CAN interface making the bus-error reporting configurable and allowing to retrieve the CAN TX and RX bus error counters via netlink interface. Here is the output of my test session showing how to use them: # ip link set can0 up type can bitrate 500000 berr-reporting on # ip -d -s link show can0 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10 link/can can <BERR-REPORTING> state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 CAN bus error counter values ^^^^^^^^^^^ bitrate 500000 sample-point 0.875 tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1 sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1 clock 8000000 re-started bus-errors arbit-lost error-warn error-pass bus-off 0 54101 0 1 1 0 RX: bytes packets errors dropped overrun mcast 432808 54101 54101 0 0 0 TX: bytes packets errors dropped carrier collsns 0 0 0 0 0 0 # ifconfig can0 down # ip link set can0 up type can berr-reporting off # candump -t d any,0:0,#FFFFFFFF (0.000000) can0 20000004 [8] 00 08 00 00 00 00 60 00 ERRORFRAME (0.000474) can0 20000004 [8] 00 20 00 00 00 00 80 00 ERRORFRAME ^^ ^^ \ \___ rxerr \_____ txerr Furthermore, the missing support for one-shot mode has been added. Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
284 lines
7.6 KiB
C
284 lines
7.6 KiB
C
/*
|
|
* iplink_can.c CAN device support
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
* Authors: Wolfgang Grandegger <wg@grandegger.com>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <linux/can/netlink.h>
|
|
|
|
#include "rt_names.h"
|
|
#include "utils.h"
|
|
#include "ip_common.h"
|
|
|
|
static void usage(void)
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: ip link set DEVICE type can\n"
|
|
"\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] | \n"
|
|
"\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n "
|
|
"\t phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n"
|
|
"\n"
|
|
"\t[ loopback { on | off } ]\n"
|
|
"\t[ listen-only { on | off } ]\n"
|
|
"\t[ triple-sampling { on | off } ]\n"
|
|
"\t[ one-shot { on | off } ]\n"
|
|
"\t[ berr-reporting { on | off } ]\n"
|
|
"\n"
|
|
"\t[ restart-ms TIME-MS ]\n"
|
|
"\t[ restart ]\n"
|
|
"\n"
|
|
"\tWhere: BITRATE := { 1..1000000 }\n"
|
|
"\t SAMPLE-POINT := { 0.000..0.999 }\n"
|
|
"\t TQ := { NUMBER }\n"
|
|
"\t PROP-SEG := { 1..8 }\n"
|
|
"\t PHASE-SEG1 := { 1..8 }\n"
|
|
"\t PHASE-SEG2 := { 1..8 }\n"
|
|
"\t SJW := { 1..4 }\n"
|
|
"\t RESTART-MS := { 0 | NUMBER }\n"
|
|
);
|
|
}
|
|
|
|
static int get_float(float *val, const char *arg)
|
|
{
|
|
float res;
|
|
char *ptr;
|
|
|
|
if (!arg || !*arg)
|
|
return -1;
|
|
res = strtof(arg, &ptr);
|
|
if (!ptr || ptr == arg || *ptr)
|
|
return -1;
|
|
*val = res;
|
|
return 0;
|
|
}
|
|
|
|
static void set_ctrlmode(char* name, char *arg,
|
|
struct can_ctrlmode *cm, __u32 flags)
|
|
{
|
|
if (strcmp(arg, "on") == 0) {
|
|
cm->flags |= flags;
|
|
} else if (strcmp(arg, "off") != 0) {
|
|
fprintf(stderr,
|
|
"Error: argument of \"%s\" must be \"on\" or \"off\"\n",
|
|
name);
|
|
exit(-1);
|
|
}
|
|
cm->mask |= flags;
|
|
}
|
|
|
|
static void print_ctrlmode(FILE *f, __u32 cm)
|
|
{
|
|
fprintf(f, "<");
|
|
#define _PF(cmflag, cmname) \
|
|
if (cm & cmflag) { \
|
|
cm &= ~cmflag; \
|
|
fprintf(f, "%s%s", cmname, cm ? "," : ""); \
|
|
}
|
|
_PF(CAN_CTRLMODE_LOOPBACK, "LOOPBACK");
|
|
_PF(CAN_CTRLMODE_LISTENONLY, "LISTEN-ONLY");
|
|
_PF(CAN_CTRLMODE_3_SAMPLES, "TRIPLE-SAMPLING");
|
|
_PF(CAN_CTRLMODE_ONE_SHOT, "ONE-SHOT");
|
|
_PF(CAN_CTRLMODE_BERR_REPORTING, "BERR-REPORTING");
|
|
#undef _PF
|
|
if (cm)
|
|
fprintf(f, "%x", cm);
|
|
fprintf(f, "> ");
|
|
}
|
|
|
|
static int can_parse_opt(struct link_util *lu, int argc, char **argv,
|
|
struct nlmsghdr *n)
|
|
{
|
|
struct can_bittiming bt;
|
|
struct can_ctrlmode cm = {0, 0};
|
|
|
|
memset(&bt, 0, sizeof(bt));
|
|
while (argc > 0) {
|
|
if (matches(*argv, "bitrate") == 0) {
|
|
NEXT_ARG();
|
|
if (get_u32(&bt.bitrate, *argv, 0))
|
|
invarg("invalid \"bitrate\" value\n", *argv);
|
|
} else if (matches(*argv, "sample-point") == 0) {
|
|
float sp;
|
|
|
|
NEXT_ARG();
|
|
if (get_float(&sp, *argv))
|
|
invarg("invalid \"sample-point\" value\n",
|
|
*argv);
|
|
bt.sample_point = (__u32)(sp * 1000);
|
|
} else if (matches(*argv, "tq") == 0) {
|
|
NEXT_ARG();
|
|
if (get_u32(&bt.tq, *argv, 0))
|
|
invarg("invalid \"tq\" value\n", *argv);
|
|
} else if (matches(*argv, "prop-seg") == 0) {
|
|
NEXT_ARG();
|
|
if (get_u32(&bt.prop_seg, *argv, 0))
|
|
invarg("invalid \"prop-seg\" value\n", *argv);
|
|
} else if (matches(*argv, "phase-seg1") == 0) {
|
|
NEXT_ARG();
|
|
if (get_u32(&bt.phase_seg1, *argv, 0))
|
|
invarg("invalid \"phase-seg1\" value\n", *argv);
|
|
} else if (matches(*argv, "phase-seg2") == 0) {
|
|
NEXT_ARG();
|
|
if (get_u32(&bt.phase_seg2, *argv, 0))
|
|
invarg("invalid \"phase-seg2\" value\n", *argv);
|
|
} else if (matches(*argv, "sjw") == 0) {
|
|
NEXT_ARG();
|
|
if (get_u32(&bt.sjw, *argv, 0))
|
|
invarg("invalid \"sjw\" value\n", *argv);
|
|
} else if (matches(*argv, "loopback") == 0) {
|
|
NEXT_ARG();
|
|
set_ctrlmode("loopback", *argv, &cm,
|
|
CAN_CTRLMODE_LOOPBACK);
|
|
} else if (matches(*argv, "listen-only") == 0) {
|
|
NEXT_ARG();
|
|
set_ctrlmode("listen-only", *argv, &cm,
|
|
CAN_CTRLMODE_LISTENONLY);
|
|
} else if (matches(*argv, "triple-sampling") == 0) {
|
|
NEXT_ARG();
|
|
set_ctrlmode("triple-sampling", *argv, &cm,
|
|
CAN_CTRLMODE_3_SAMPLES);
|
|
} else if (matches(*argv, "one-shot") == 0) {
|
|
NEXT_ARG();
|
|
set_ctrlmode("one-shot", *argv, &cm,
|
|
CAN_CTRLMODE_ONE_SHOT);
|
|
} else if (matches(*argv, "berr-reporting") == 0) {
|
|
NEXT_ARG();
|
|
set_ctrlmode("berr-reporting", *argv, &cm,
|
|
CAN_CTRLMODE_BERR_REPORTING);
|
|
} else if (matches(*argv, "restart") == 0) {
|
|
__u32 val = 1;
|
|
|
|
addattr32(n, 1024, IFLA_CAN_RESTART, val);
|
|
} else if (matches(*argv, "restart-ms") == 0) {
|
|
__u32 val;
|
|
|
|
NEXT_ARG();
|
|
if (get_u32(&val, *argv, 0))
|
|
invarg("invalid \"restart-ms\" value\n", *argv);
|
|
addattr32(n, 1024, IFLA_CAN_RESTART_MS, val);
|
|
} else if (matches(*argv, "help") == 0) {
|
|
usage();
|
|
return -1;
|
|
} else {
|
|
fprintf(stderr, "can: what is \"%s\"?\n", *argv);
|
|
usage();
|
|
return -1;
|
|
}
|
|
argc--, argv++;
|
|
}
|
|
|
|
if (bt.bitrate || bt.tq)
|
|
addattr_l(n, 1024, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
|
|
if (cm.mask)
|
|
addattr_l(n, 1024, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *can_state_names[] = {
|
|
[CAN_STATE_ERROR_ACTIVE] = "ERROR-ACTIVE",
|
|
[CAN_STATE_ERROR_WARNING] = "ERROR-WARNING",
|
|
[CAN_STATE_ERROR_PASSIVE] = "ERROR-PASSIVE",
|
|
[CAN_STATE_BUS_OFF] = "BUS-OFF",
|
|
[CAN_STATE_STOPPED] = "STOPPED",
|
|
[CAN_STATE_SLEEPING] = "SLEEPING"
|
|
};
|
|
|
|
static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
|
|
{
|
|
if (!tb)
|
|
return;
|
|
|
|
if (tb[IFLA_CAN_CTRLMODE]) {
|
|
struct can_ctrlmode *cm = RTA_DATA(tb[IFLA_CAN_CTRLMODE]);
|
|
|
|
if (cm->flags)
|
|
print_ctrlmode(f, cm->flags);
|
|
}
|
|
|
|
if (tb[IFLA_CAN_STATE]) {
|
|
int *state = RTA_DATA(tb[IFLA_CAN_STATE]);
|
|
|
|
fprintf(f, "state %s ", *state <= CAN_STATE_MAX ?
|
|
can_state_names[*state] : "UNKNOWN");
|
|
}
|
|
|
|
if (tb[IFLA_CAN_BERR_COUNTER]) {
|
|
struct can_berr_counter *bc =
|
|
RTA_DATA(tb[IFLA_CAN_BERR_COUNTER]);
|
|
|
|
fprintf(f, "(berr-counter tx %d rx %d) ", bc->txerr, bc->rxerr);
|
|
}
|
|
|
|
if (tb[IFLA_CAN_RESTART_MS]) {
|
|
__u32 *restart_ms = RTA_DATA(tb[IFLA_CAN_RESTART_MS]);
|
|
|
|
fprintf(f, "restart-ms %d ", *restart_ms);
|
|
}
|
|
|
|
if (tb[IFLA_CAN_BITTIMING]) {
|
|
struct can_bittiming *bt = RTA_DATA(tb[IFLA_CAN_BITTIMING]);
|
|
|
|
fprintf(f, "\n "
|
|
"bitrate %d sample-point %.3f ",
|
|
bt->bitrate, (float)bt->sample_point / 1000.);
|
|
fprintf(f, "\n "
|
|
"tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d",
|
|
bt->tq, bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
|
|
bt->sjw);
|
|
}
|
|
|
|
if (tb[IFLA_CAN_BITTIMING_CONST]) {
|
|
struct can_bittiming_const *btc =
|
|
RTA_DATA(tb[IFLA_CAN_BITTIMING_CONST]);
|
|
|
|
fprintf(f, "\n "
|
|
"%s: tseg1 %d..%d tseg2 %d..%d "
|
|
"sjw 1..%d brp %d..%d brp-inc %d",
|
|
btc->name, btc->tseg1_min, btc->tseg1_max,
|
|
btc->tseg2_min, btc->tseg2_max, btc->sjw_max,
|
|
btc->brp_min, btc->brp_max, btc->brp_inc);
|
|
}
|
|
|
|
if (tb[IFLA_CAN_CLOCK]) {
|
|
struct can_clock *clock = RTA_DATA(tb[IFLA_CAN_CLOCK]);
|
|
|
|
fprintf(f, "\n clock %d", clock->freq);
|
|
}
|
|
|
|
}
|
|
|
|
static void can_print_xstats(struct link_util *lu,
|
|
FILE *f, struct rtattr *xstats)
|
|
{
|
|
struct can_device_stats *stats;
|
|
|
|
if (xstats && RTA_PAYLOAD(xstats) == sizeof(*stats)) {
|
|
stats = RTA_DATA(xstats);
|
|
fprintf(f, "\n "
|
|
"re-started bus-errors arbit-lost "
|
|
"error-warn error-pass bus-off");
|
|
fprintf(f, "\n %-10d %-10d %-10d %-10d %-10d %-10d",
|
|
stats->restarts, stats->bus_error,
|
|
stats->arbitration_lost, stats->error_warning,
|
|
stats->error_passive, stats->bus_off);
|
|
}
|
|
}
|
|
|
|
struct link_util can_link_util = {
|
|
.id = "can",
|
|
.maxattr = IFLA_CAN_MAX,
|
|
.parse_opt = can_parse_opt,
|
|
.print_opt = can_print_opt,
|
|
.print_xstats = can_print_xstats,
|
|
};
|