]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd-link: add support to configure CAN interfaces 9185/head
authorHiram van Paassen <hiram.van.paassen@mastervolt.com>
Tue, 10 Apr 2018 15:26:20 +0000 (17:26 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Sat, 9 Jun 2018 13:12:31 +0000 (15:12 +0200)
This patch adds support for kind "can". Fixes: #4042.

man/systemd.network.xml
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-netlink/netlink-types.h
src/network/networkd-link.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h

index 96ccf5f637d6837878ca6c36fff0b96368e12eb3..d7bcf5a06754fc5aceb6b547e96ee6353506d1e0 100644 (file)
         </varlistentry>
       </variablelist>
   </refsect1>
+
+  <refsect1>
+    <title>[CAN] Section Options</title>
+      <para>The <literal>[CAN]</literal> section manages the Controller Area Network (CAN bus) and accepts the
+      following keys.</para>
+      <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>BitRate=</varname></term>
+          <listitem>
+            <para>The bitrate of CAN device in bits per second. The usual SI prefixes (K, M) with the base of 1000 can
+            be used here.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>SamplePoint=</varname></term>
+          <listitem>
+            <para>Optional sample point in percent with one decimal (e.g. <literal>75%</literal>,
+            <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>).</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>RestartSec=</varname></term>
+          <listitem>
+            <para>Automatic restart delay time. If set to a non-zero value, a restart of the CAN controller will be
+            triggered automatically in case of a bus-off condition after the specified delay time. Subsecond delays can
+            be specified using decimals (e.g. <literal>0.1s</literal>) or a <literal>ms</literal> or
+            <literal>us</literal> postfix. Using <literal>infinity</literal> or <literal>0</literal> will turn the
+            automatic restart off. By default automatic restart is disabled.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>[BridgeVLAN] Section Options</title>
       <para>The <literal>[BridgeVLAN]</literal> section manages the VLAN ID configuration of a bridge port and accepts
index ddb4d90eaf2e82b930257e8bb19eff5716503c35..a82b1e1a89018d755aa5f2ff6cdc3431e8baa3e2 100644 (file)
@@ -300,6 +300,11 @@ static const NLType rtnl_link_info_data_geneve_types[] = {
         [IFLA_GENEVE_LABEL]             = { .type = NETLINK_TYPE_U32 },
 };
 
+static const NLType rtnl_link_info_data_can_types[] = {
+        [IFLA_CAN_BITTIMING]            = { .size = sizeof(struct can_bittiming) },
+        [IFLA_CAN_RESTART_MS]           = { .type = NETLINK_TYPE_U32 },
+};
+
 /* these strings must match the .kind entries in the kernel */
 static const char* const nl_union_link_info_data_table[] = {
         [NL_UNION_LINK_INFO_DATA_BOND] = "bond",
@@ -326,6 +331,7 @@ static const char* const nl_union_link_info_data_table[] = {
         [NL_UNION_LINK_INFO_DATA_VXCAN] = "vxcan",
         [NL_UNION_LINK_INFO_DATA_WIREGUARD] = "wireguard",
         [NL_UNION_LINK_INFO_DATA_NETDEVSIM] = "netdevsim",
+        [NL_UNION_LINK_INFO_DATA_CAN] = "can",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
@@ -371,6 +377,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
                                                        .types = rtnl_link_info_data_geneve_types },
         [NL_UNION_LINK_INFO_DATA_VXCAN] =            { .count = ELEMENTSOF(rtnl_link_info_data_vxcan_types),
                                                        .types = rtnl_link_info_data_vxcan_types },
+        [NL_UNION_LINK_INFO_DATA_CAN] =              { .count = ELEMENTSOF(rtnl_link_info_data_can_types),
+                                                       .types = rtnl_link_info_data_can_types },
 };
 
 static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
index a7542eb33d11297c975713bcf972169c9457cfd2..559976c24c28d2cbc3c32f8cdcf2c2aed657aa6f 100644 (file)
@@ -83,6 +83,7 @@ typedef enum NLUnionLinkInfoData {
         NL_UNION_LINK_INFO_DATA_VXCAN,
         NL_UNION_LINK_INFO_DATA_WIREGUARD,
         NL_UNION_LINK_INFO_DATA_NETDEVSIM,
+        NL_UNION_LINK_INFO_DATA_CAN,
         _NL_UNION_LINK_INFO_DATA_MAX,
         _NL_UNION_LINK_INFO_DATA_INVALID = -1
 } NLUnionLinkInfoData;
index b36c383d37e5e3ff88e0ab61dc2c27676fc870fa..c0496407abeb5d5d4f2ff44d4050131050c8c553 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <netinet/ether.h>
 #include <linux/if.h>
+#include <linux/can/netlink.h>
 #include <unistd.h>
 #include <stdio_ext.h>
 
@@ -1872,6 +1873,105 @@ static int link_up_can(Link *link) {
         return 0;
 }
 
+static int link_set_can(Link *link) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+
+        log_link_debug(link, "link_set_can");
+
+        r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to allocate netlink message: %m");
+
+        r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not set netlink flags: %m");
+
+        r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to open netlink container: %m");
+
+        r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m");
+
+        if (link->network->can_bitrate > 0 || link->network->can_sample_point > 0) {
+                struct can_bittiming bt = {
+                        .bitrate = link->network->can_bitrate,
+                        .sample_point = link->network->can_sample_point,
+                };
+
+                if (link->network->can_bitrate > UINT32_MAX) {
+                        log_link_error(link, "bitrate (%zu) too big.", link->network->can_bitrate);
+                        return -ERANGE;
+                }
+
+                log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate);
+                if (link->network->can_sample_point > 0)
+                        log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10);
+                else
+                        log_link_debug(link, "Using default sample point");
+
+                r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m");
+        }
+
+        if (link->network->can_restart_us > 0) {
+                char time_string[FORMAT_TIMESPAN_MAX];
+                uint64_t restart_ms;
+
+                if (link->network->can_restart_us == USEC_INFINITY)
+                        restart_ms = 0;
+                else
+                        restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC);
+
+                format_timespan(time_string, FORMAT_TIMESPAN_MAX, restart_ms * 1000, MSEC_PER_SEC);
+
+                if (restart_ms > UINT32_MAX) {
+                        log_link_error(link, "restart timeout (%s) too big.", time_string);
+                        return -ERANGE;
+                }
+
+                log_link_debug(link, "Setting restart = %s", time_string);
+
+                r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m");
+        }
+
+        r = sd_netlink_message_close_container(m);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to close netlink container: %m");
+
+        r = sd_netlink_message_close_container(m);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to close netlink container: %m");
+
+        r = sd_netlink_call_async(link->manager->rtnl, m, link_set_handler, link,  0, NULL);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+
+        if (!(link->flags & IFF_UP)) {
+                r = link_up_can(link);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return r;
+                }
+        }
+
+        log_link_debug(link, "link_set_can done");
+
+        return r;
+}
+
 static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
         _cleanup_(link_unrefp) Link *link = userdata;
         int r;
@@ -1885,6 +1985,11 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *user
         if (r < 0)
                 log_link_warning_errno(link, r, "Could not bring down interface: %m");
 
+        if (streq_ptr(link->kind, "can")) {
+                link_ref(link);
+                link_set_can(link);
+        }
+
         return 1;
 }
 
@@ -2602,6 +2707,21 @@ static int link_update_lldp(Link *link) {
 static int link_configure_can(Link *link) {
         int r;
 
+        if (streq_ptr(link->kind, "can")) {
+                /* The CAN interface must be down to configure bitrate, etc... */
+                if ((link->flags & IFF_UP)) {
+                        r = link_down(link);
+                        if (r < 0) {
+                                link_enter_failed(link);
+                                return r;
+                        }
+
+                        return 0;
+                }
+
+                return link_set_can(link);
+        }
+
         if (!(link->flags & IFF_UP)) {
                 r = link_up_can(link);
                 if (r < 0) {
@@ -2620,7 +2740,7 @@ static int link_configure(Link *link) {
         assert(link->network);
         assert(link->state == LINK_STATE_PENDING);
 
-        if (streq_ptr(link->kind, "vcan"))
+        if (STRPTR_IN_SET(link->kind, "can", "vcan"))
                 return link_configure_can(link);
 
         /* Drop foreign config, but ignore loopback or critical devices.
index 7e625e48fa467d4e9b030a5fd4cad5a45384dccb..e6ca6631ed7ab4c9ce176cb95a3629d593baf779 100644 (file)
@@ -178,6 +178,9 @@ IPv6Prefix.OnLink,                      config_parse_prefix_flags,
 IPv6Prefix.AddressAutoconfiguration,    config_parse_prefix_flags,                      0,                             0
 IPv6Prefix.ValidLifetimeSec,            config_parse_prefix_lifetime,                   0,                             0
 IPv6Prefix.PreferredLifetimeSec,        config_parse_prefix_lifetime,                   0,                             0
+CAN.BitRate,                            config_parse_si_size,                           0,                             offsetof(Network, can_bitrate)
+CAN.SamplePoint,                        config_parse_permille,                          0,                             offsetof(Network, can_sample_point)
+CAN.RestartSec,                         config_parse_sec,                               0,                             offsetof(Network, can_restart_us)
 /* backwards compatibility: do not add new entries to this section */
 Network.IPv4LL,                         config_parse_ipv4ll,                            0,                             offsetof(Network, link_local)
 DHCPv4.UseDNS,                          config_parse_bool,                              0,                             offsetof(Network, dhcp_use_dns)
index c3a11ddca07a7ef8ad55a38e3833f1c0a59360c4..b2a75c7e98355ac0d506618f9618cbe345e15c47 100644 (file)
@@ -272,7 +272,8 @@ static int network_load_one(Manager *manager, const char *filename) {
                               "BridgeFDB\0"
                               "BridgeVLAN\0"
                               "IPv6PrefixDelegation\0"
-                              "IPv6Prefix\0",
+                              "IPv6Prefix\0"
+                              "CAN\0",
                               config_item_perf_lookup, network_network_gperf_lookup,
                               CONFIG_PARSE_WARN, network);
         if (r < 0)
index b8e2c523a3c26b96682e2104c028a36b11b22676..5b6b40d5da3b666162c2968788f71552c8276251 100644 (file)
@@ -191,6 +191,11 @@ struct Network {
         uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN];
         uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN];
 
+        /* CAN support */
+        size_t can_bitrate;
+        unsigned can_sample_point;
+        usec_t can_restart_us;
+
         AddressFamilyBoolean ip_forward;
         bool ip_masquerade;