From 913311fc15a222de1e4754bff84afcd9e64408e2 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Sun, 17 Jan 2021 19:31:27 +0100 Subject: [PATCH] can-calc-bit-timing: add support to decode user supplied bit timing parameters Signed-off-by: Marc Kleine-Budde --- can-calc-bit-timing.c | 171 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 152 insertions(+), 19 deletions(-) diff --git a/can-calc-bit-timing.c b/can-calc-bit-timing.c index 7fbc1ef..5885543 100644 --- a/can-calc-bit-timing.c +++ b/can-calc-bit-timing.c @@ -2,7 +2,7 @@ /* can-calc-bit-timing.c: Calculate CAN bit timing parameters * * Copyright (C) 2008 Wolfgang Grandegger - * Copyright (C) 2016 Marc Kleine-Budde + * Copyright (C) 2016, 2021 Marc Kleine-Budde * * Derived from: * can_baud.c - CAN baudrate calculation @@ -29,6 +29,17 @@ #include #include +enum { + OPT_TQ = UCHAR_MAX + 1, + OPT_PROP_SEG, + OPT_PHASE_SEG1, + OPT_PHASE_SEG2, + OPT_SJW, + OPT_BRP, + OPT_TSEG1, + OPT_TSEG2, +}; + /* imported from kernel */ /** @@ -140,7 +151,17 @@ static void print_usage(char *cmd) "\t-b bit-rate in bits/sec\n" "\t-s sample-point in one-tenth of a percent\n" "\t or 0 for CIA recommended sample points\n" - "\t-c real CAN system clock in Hz\n", + "\t-c real CAN system clock in Hz\n" + "\n" + "Or supply low level bit timing parameters to decode them:\n" + "\n" + "\t--prop-seg Propagation segment in TQs\n" + "\t--phase-seg1 Phase buffer segment 1 in TQs\n" + "\t--phase-seg2 Phase buffer segment 2 in TQs\n" + "\t--sjw Synchronisation jump width in TQs\n" + "\t--brp Bit-rate prescaler\n" + "\t--tseg1 Time segment 1 = prop-seg + phase-seg1\n" + "\t--tseg2 Time segment 2 = phase_seg2\n", cmd); } @@ -569,6 +590,46 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, return 0; } +static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt, + const struct can_bittiming_const *btc) +{ + struct can_priv *priv = netdev_priv(dev); + int tseg1, alltseg; + u64 brp64, v64; + + tseg1 = bt->prop_seg + bt->phase_seg1; + if (!bt->sjw) + bt->sjw = 1; + if (bt->sjw > btc->sjw_max || + tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max || + bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max) + return -ERANGE; + + if (!bt->brp) { + brp64 = (u64)priv->clock.freq * (u64)bt->tq; + if (btc->brp_inc > 1) + do_div(brp64, btc->brp_inc); + brp64 += 500000000UL - 1; + do_div(brp64, 1000000000UL); /* the practicable BRP */ + if (btc->brp_inc > 1) + brp64 *= btc->brp_inc; + bt->brp = brp64; + } + + v64 = bt->brp * 1000 * 1000 * 1000; + do_div(v64, priv->clock.freq); + bt->tq = v64; + + if (bt->brp < btc->brp_min || bt->brp > btc->brp_max) + return -EINVAL; + + alltseg = CAN_CALC_SYNC_SEG + bt->prop_seg + bt->phase_seg1 + bt->phase_seg2; + bt->bitrate = priv->clock.freq / (bt->brp * alltseg); + bt->sample_point = ((CAN_CALC_SYNC_SEG + tseg1) * 1000) / alltseg; + + return 0; +} + static __u32 get_cia_sample_point(__u32 bitrate) { __u32 sampl_pt; @@ -584,6 +645,7 @@ static __u32 get_cia_sample_point(__u32 bitrate) } static void print_bit_timing(const struct calc_bittiming_const *btc, + const struct can_bittiming *ref_bt, const struct calc_ref_clk *ref_clk, unsigned int bitrate_nominal, unsigned int spt_nominal, @@ -613,9 +675,18 @@ static void print_bit_timing(const struct calc_bittiming_const *btc, printf("\n"); } - if (can_calc_bittiming(&dev, &bt, &btc->bittiming_const)) { - printf("%7d ***bitrate not possible***\n", bitrate_nominal); - return; + if (ref_bt) { + bt = *ref_bt; + + if (can_fixup_bittiming(&dev, &bt, &btc->bittiming_const)) { + printf("%7d ***parameters exceed controller's range***\n", bitrate_nominal); + return; + } + } else { + if (can_calc_bittiming(&dev, &bt, &btc->bittiming_const)) { + printf("%7d ***bitrate not possible***\n", bitrate_nominal); + return; + } } /* get nominal sample point */ @@ -625,21 +696,30 @@ static void print_bit_timing(const struct calc_bittiming_const *btc, rate_error = abs(bitrate_nominal - bt.bitrate); spt_error = abs(spt_nominal - bt.sample_point); - printf("%7d " - "%6d %3d %4d %4d " - "%3d %3d " - "%7d %4.1f%% " - "%4.1f%% %4.1f%% %4.1f%% ", + printf("%7d " /* Bitrate */ + "%6d %3d %4d %4d " /* TQ[ns], PrS, PhS1, PhS2 */ + "%3d %3d " /* SJW, BRP */ + "%7d ", /* real Bitrate */ bitrate_nominal, bt.tq, bt.prop_seg, bt.phase_seg1, bt.phase_seg2, bt.sjw, bt.brp, + bt.bitrate); - bt.bitrate, - 100.0 * rate_error / bitrate_nominal, + if (100.0 * rate_error / bitrate_nominal > 99.9) + printf("≥100%% "); + else + printf("%4.1f%% ", + 100.0 * rate_error / bitrate_nominal); + printf("%4.1f%% %4.1f%% ", /* nom SampP, real SampP */ spt_nominal / 10.0, - bt.sample_point / 10.0, - 100.0 * spt_error / spt_nominal); + bt.sample_point / 10.0); + + if (100.0 * spt_error / spt_nominal > 99.9) + printf("≥100%% "); + else + printf("%4.1f%% ", /* SampP Error */ + 100.0 * spt_error / spt_nominal); if (btc->printf_btr) btc->printf_btr(&bt, false); @@ -655,6 +735,7 @@ static void do_list(void) } static void do_calc(const char *name, + const struct can_bittiming *opt_ref_bt, __u32 bitrate_nominal, unsigned int spt_nominal, struct calc_ref_clk *opt_ref_clk, bool quiet) { @@ -681,11 +762,11 @@ static void do_calc(const char *name, break; if (bitrate_nominal) { - print_bit_timing(btc, ref_clk, bitrate_nominal, + print_bit_timing(btc, opt_ref_bt, ref_clk, bitrate_nominal, spt_nominal, quiet); } else { for (k = 0; k < ARRAY_SIZE(common_bitrates); k++) - print_bit_timing(btc, ref_clk, + print_bit_timing(btc, opt_ref_bt, ref_clk, common_bitrates[k], spt_nominal, k); } @@ -710,12 +791,24 @@ int main(int argc, char *argv[]) struct calc_ref_clk opt_ref_clk = { .name = "cmd-line", }; + struct can_bittiming bt = { 0 }; bool quiet = false, list = false; const char *name = NULL; int opt; + const struct option long_options[] = { + { "tq", required_argument, 0, OPT_TQ, }, + { "prop-seg", required_argument, 0, OPT_PROP_SEG, }, + { "phase-seg1", required_argument, 0, OPT_PHASE_SEG1, }, + { "phase-seg2", required_argument, 0, OPT_PHASE_SEG2, }, + { "sjw", required_argument, 0, OPT_SJW, }, + { "brp", required_argument, 0, OPT_BRP, }, + { "tseg1", required_argument, 0, OPT_TSEG1, }, + { "tseg2", required_argument, 0, OPT_TSEG2, }, + { 0, 0, 0, 0 }, + }; - while ((opt = getopt(argc, argv, "b:c:lqs:?")) != -1) { + while ((opt = getopt_long(argc, argv, "b:c:lqs:?", long_options, NULL)) != -1) { switch (opt) { case 'b': bitrate_nominal = atoi(optarg); @@ -742,6 +835,43 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); break; + case OPT_TQ: + bt.tq = strtoul(optarg, NULL, 10); + break; + + case OPT_PROP_SEG: + bt.prop_seg = strtoul(optarg, NULL, 10); + break; + + case OPT_PHASE_SEG1: + bt.phase_seg1 = strtoul(optarg, NULL, 10); + break; + + case OPT_PHASE_SEG2: + bt.phase_seg2 = strtoul(optarg, NULL, 10); + break; + + case OPT_SJW: + bt.sjw = strtoul(optarg, NULL, 10); + break; + + case OPT_BRP: + bt.brp = strtoul(optarg, NULL, 10); + break; + + case OPT_TSEG1: { + __u32 tseg1; + + tseg1 = strtoul(optarg, NULL, 10); + bt.prop_seg = tseg1 / 2; + bt.phase_seg1 = tseg1 - bt.prop_seg; + break; + } + + case OPT_TSEG2: + bt.phase_seg2 = strtoul(optarg, NULL, 10); + break; + default: print_usage(basename(argv[0])); exit(EXIT_FAILURE); @@ -767,8 +897,11 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - do_calc(name, bitrate_nominal, spt_nominal, - opt_ref_clk.clk ? &opt_ref_clk : NULL, quiet); + do_calc(name, + bt.prop_seg ? &bt: NULL, + bitrate_nominal, spt_nominal, + opt_ref_clk.clk ? &opt_ref_clk : NULL, + quiet); exit(EXIT_SUCCESS); }