diff --git a/ip/iplink_can.c b/ip/iplink_can.c index cf6b06b8..f4b37528 100644 --- a/ip/iplink_can.c +++ b/ip/iplink_can.c @@ -28,6 +28,7 @@ static void print_usage(FILE *f) "\n" "\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] |\n" "\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n \t dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n" + "\t[ tdcv TDCV tdco TDCO tdcf TDCF ]\n" "\n" "\t[ loopback { on | off } ]\n" "\t[ listen-only { on | off } ]\n" @@ -38,6 +39,7 @@ static void print_usage(FILE *f) "\t[ fd-non-iso { on | off } ]\n" "\t[ presume-ack { on | off } ]\n" "\t[ cc-len8-dlc { on | off } ]\n" + "\t[ tdc-mode { auto | manual | off } ]\n" "\n" "\t[ restart-ms TIME-MS ]\n" "\t[ restart ]\n" @@ -51,6 +53,9 @@ static void print_usage(FILE *f) "\t PHASE-SEG1 := { NUMBER in tq }\n" "\t PHASE-SEG2 := { NUMBER in tq }\n" "\t SJW := { NUMBER in tq }\n" + "\t TDCV := { NUMBER in tc}\n" + "\t TDCO := { NUMBER in tc}\n" + "\t TDCF := { NUMBER in tc}\n" "\t RESTART-MS := { 0 | NUMBER in ms }\n" ); } @@ -113,6 +118,8 @@ static void print_ctrlmode(enum output_type t, __u32 flags, const char* key) print_flag(t, &flags, CAN_CTRLMODE_FD_NON_ISO, "FD-NON-ISO"); print_flag(t, &flags, CAN_CTRLMODE_PRESUME_ACK, "PRESUME-ACK"); print_flag(t, &flags, CAN_CTRLMODE_CC_LEN8_DLC, "CC-LEN8-DLC"); + print_flag(t, &flags, CAN_CTRLMODE_TDC_AUTO, "TDC-AUTO"); + print_flag(t, &flags, CAN_CTRLMODE_TDC_MANUAL, "TDC-MANUAL"); if (flags) print_hex(t, NULL, "%x", flags); @@ -125,6 +132,8 @@ static int can_parse_opt(struct link_util *lu, int argc, char **argv, { struct can_bittiming bt = {}, dbt = {}; struct can_ctrlmode cm = { 0 }; + struct rtattr *tdc; + __u32 tdcv = -1, tdco = -1, tdcf = -1; while (argc > 0) { if (matches(*argv, "bitrate") == 0) { @@ -190,6 +199,18 @@ static int can_parse_opt(struct link_util *lu, int argc, char **argv, NEXT_ARG(); if (get_u32(&dbt.sjw, *argv, 0)) invarg("invalid \"dsjw\" value\n", *argv); + } else if (matches(*argv, "tdcv") == 0) { + NEXT_ARG(); + if (get_u32(&tdcv, *argv, 0)) + invarg("invalid \"tdcv\" value\n", *argv); + } else if (matches(*argv, "tdco") == 0) { + NEXT_ARG(); + if (get_u32(&tdco, *argv, 0)) + invarg("invalid \"tdco\" value\n", *argv); + } else if (matches(*argv, "tdcf") == 0) { + NEXT_ARG(); + if (get_u32(&tdcf, *argv, 0)) + invarg("invalid \"tdcf\" value\n", *argv); } else if (matches(*argv, "loopback") == 0) { NEXT_ARG(); set_ctrlmode("loopback", *argv, &cm, @@ -226,6 +247,23 @@ static int can_parse_opt(struct link_util *lu, int argc, char **argv, NEXT_ARG(); set_ctrlmode("cc-len8-dlc", *argv, &cm, CAN_CTRLMODE_CC_LEN8_DLC); + } else if (matches(*argv, "tdc-mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "auto") == 0) { + cm.flags |= CAN_CTRLMODE_TDC_AUTO; + cm.mask |= CAN_CTRLMODE_TDC_AUTO; + } else if (strcmp(*argv, "manual") == 0) { + cm.flags |= CAN_CTRLMODE_TDC_MANUAL; + cm.mask |= CAN_CTRLMODE_TDC_MANUAL; + } else if (strcmp(*argv, "off") == 0) { + cm.mask |= CAN_CTRLMODE_TDC_AUTO | + CAN_CTRLMODE_TDC_MANUAL; + } else { + fprintf(stderr, + "Error: argument of \"tdc-mode\" must be \"auto\", \"manual\" or \"off\", not \"%s\"\n", + *argv); + exit (-1); + } } else if (matches(*argv, "restart") == 0) { __u32 val = 1; @@ -263,6 +301,17 @@ static int can_parse_opt(struct link_util *lu, int argc, char **argv, if (cm.mask) addattr_l(n, 1024, IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); + if (tdcv != -1 || tdco != -1 || tdcf != -1) { + tdc = addattr_nest(n, 1024, IFLA_CAN_TDC | NLA_F_NESTED); + if (tdcv != -1) + addattr32(n, 1024, IFLA_CAN_TDC_TDCV, tdcv); + if (tdco != -1) + addattr32(n, 1024, IFLA_CAN_TDC_TDCO, tdco); + if (tdcf != -1) + addattr32(n, 1024, IFLA_CAN_TDC_TDCF, tdcf); + addattr_nest_end(n, tdc); + } + return 0; } @@ -291,6 +340,62 @@ static void can_print_timing_min_max(const char *json_attr, const char *fp_attr, close_json_object(); } +static void can_print_tdc_opt(FILE *f, struct rtattr *tdc_attr) +{ + struct rtattr *tb[IFLA_CAN_TDC_MAX + 1]; + + parse_rtattr_nested(tb, IFLA_CAN_TDC_MAX, tdc_attr); + if (tb[IFLA_CAN_TDC_TDCV] || tb[IFLA_CAN_TDC_TDCO] || + tb[IFLA_CAN_TDC_TDCF]) { + open_json_object("tdc"); + can_print_nl_indent(); + if (tb[IFLA_CAN_TDC_TDCV]) { + __u32 *tdcv = RTA_DATA(tb[IFLA_CAN_TDC_TDCV]); + + print_uint(PRINT_ANY, "tdcv", " tdcv %u", *tdcv); + } + if (tb[IFLA_CAN_TDC_TDCO]) { + __u32 *tdco = RTA_DATA(tb[IFLA_CAN_TDC_TDCO]); + + print_uint(PRINT_ANY, "tdco", " tdco %u", *tdco); + } + if (tb[IFLA_CAN_TDC_TDCF]) { + __u32 *tdcf = RTA_DATA(tb[IFLA_CAN_TDC_TDCF]); + + print_uint(PRINT_ANY, "tdcf", " tdcf %u", *tdcf); + } + close_json_object(); + } +} + +static void can_print_tdc_const_opt(FILE *f, struct rtattr *tdc_attr) +{ + struct rtattr *tb[IFLA_CAN_TDC_MAX + 1]; + + parse_rtattr_nested(tb, IFLA_CAN_TDC_MAX, tdc_attr); + open_json_object("tdc"); + can_print_nl_indent(); + if (tb[IFLA_CAN_TDC_TDCV_MIN] && tb[IFLA_CAN_TDC_TDCV_MAX]) { + __u32 *tdcv_min = RTA_DATA(tb[IFLA_CAN_TDC_TDCV_MIN]); + __u32 *tdcv_max = RTA_DATA(tb[IFLA_CAN_TDC_TDCV_MAX]); + + can_print_timing_min_max("tdcv", " tdcv", *tdcv_min, *tdcv_max); + } + if (tb[IFLA_CAN_TDC_TDCO_MIN] && tb[IFLA_CAN_TDC_TDCO_MAX]) { + __u32 *tdco_min = RTA_DATA(tb[IFLA_CAN_TDC_TDCO_MIN]); + __u32 *tdco_max = RTA_DATA(tb[IFLA_CAN_TDC_TDCO_MAX]); + + can_print_timing_min_max("tdco", " tdco", *tdco_min, *tdco_max); + } + if (tb[IFLA_CAN_TDC_TDCF_MIN] && tb[IFLA_CAN_TDC_TDCF_MAX]) { + __u32 *tdcf_min = RTA_DATA(tb[IFLA_CAN_TDC_TDCF_MIN]); + __u32 *tdcf_max = RTA_DATA(tb[IFLA_CAN_TDC_TDCF_MAX]); + + can_print_timing_min_max("tdcf", " tdcf", *tdcf_min, *tdcf_max); + } + close_json_object(); +} + static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { if (!tb) @@ -421,6 +526,10 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) dbt->phase_seg2); print_uint(PRINT_ANY, "sjw", " dsjw %u", dbt->sjw); print_uint(PRINT_ANY, "brp", " dbrp %u", dbt->brp); + + if (tb[IFLA_CAN_TDC]) + can_print_tdc_opt(f, tb[IFLA_CAN_TDC]); + close_json_object(); } @@ -441,6 +550,10 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) can_print_timing_min_max("brp", " dbrp", dbtc->brp_min, dbtc->brp_max); print_uint(PRINT_ANY, "brp_inc", " dbrp_inc %u", dbtc->brp_inc); + + if (tb[IFLA_CAN_TDC]) + can_print_tdc_const_opt(f, tb[IFLA_CAN_TDC]); + close_json_object(); }