can: dev: add CAN interface termination API
authorOliver Hartkopp <socketcan@hartkopp.net>
Tue, 10 Jan 2017 17:52:06 +0000 (18:52 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Tue, 24 Jan 2017 12:52:00 +0000 (13:52 +0100)
This patch adds a netlink interface to configure the CAN bus termination of
CAN interfaces.

Inside the driver an array of supported termination values is defined:

const u16 drvname_termination[] = { 60, 120, CAN_TERMINATION_DISABLED };

struct drvname_priv *priv;
priv = netdev_priv(dev);

priv->termination_const = drvname_termination;
priv->termination_const_cnt = ARRAY_SIZE(drvname_termination);
priv->termination = CAN_TERMINATION_DISABLED;

And the funtion to set the value has to be defined:

priv->do_set_termination = drvname_set_termination;

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Reviewed-by: Ramesh Shanmugasundaram <Ramesh.shanmugasundaram@bp.renesas.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/dev.c
include/linux/can/dev.h
include/uapi/linux/can/netlink.h

index 8d6208c0b4003a9b19646e7d653a9501b6660e3c..fefe2cd17721ea7b39c8440ac9b8d7eee560000b 100644 (file)
@@ -958,6 +958,30 @@ static int can_changelink(struct net_device *dev,
                }
        }
 
+       if (data[IFLA_CAN_TERMINATION]) {
+               const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]);
+               const unsigned int num_term = priv->termination_const_cnt;
+               unsigned int i;
+
+               if (!priv->do_set_termination)
+                       return -EOPNOTSUPP;
+
+               /* check whether given value is supported by the interface */
+               for (i = 0; i < num_term; i++) {
+                       if (termval == priv->termination_const[i])
+                               break;
+               }
+               if (i >= num_term)
+                       return -EINVAL;
+
+               /* Finally, set the termination value */
+               err = priv->do_set_termination(dev, termval);
+               if (err)
+                       return err;
+
+               priv->termination = termval;
+       }
+
        return 0;
 }
 
@@ -980,6 +1004,11 @@ static size_t can_get_size(const struct net_device *dev)
                size += nla_total_size(sizeof(struct can_bittiming));
        if (priv->data_bittiming_const)                         /* IFLA_CAN_DATA_BITTIMING_CONST */
                size += nla_total_size(sizeof(struct can_bittiming_const));
+       if (priv->termination_const) {
+               size += nla_total_size(sizeof(priv->termination));              /* IFLA_CAN_TERMINATION */
+               size += nla_total_size(sizeof(*priv->termination_const) *       /* IFLA_CAN_TERMINATION_CONST */
+                                      priv->termination_const_cnt);
+       }
 
        return size;
 }
@@ -1018,7 +1047,15 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
            (priv->data_bittiming_const &&
             nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST,
                     sizeof(*priv->data_bittiming_const),
-                    priv->data_bittiming_const)))
+                    priv->data_bittiming_const)) ||
+
+           (priv->termination_const &&
+            (nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) ||
+             nla_put(skb, IFLA_CAN_TERMINATION_CONST,
+                     sizeof(*priv->termination_const) *
+                     priv->termination_const_cnt,
+                     priv->termination_const))))
+
                return -EMSGSIZE;
 
        return 0;
@@ -1073,6 +1110,16 @@ static struct rtnl_link_ops can_link_ops __read_mostly = {
  */
 int register_candev(struct net_device *dev)
 {
+       struct can_priv *priv = netdev_priv(dev);
+
+       /* Ensure termination_const, termination_const_cnt and
+        * do_set_termination consistency. All must be either set or
+        * unset.
+        */
+       if ((!priv->termination_const != !priv->termination_const_cnt) ||
+           (!priv->termination_const != !priv->do_set_termination))
+               return -EINVAL;
+
        dev->rtnl_link_ops = &can_link_ops;
        return register_netdev(dev);
 }
index 5f5270941ba02ae78a81129dfff17d647ef3099c..f6a57f322f00ea57d16621004eecf01de983f324 100644 (file)
@@ -38,6 +38,9 @@ struct can_priv {
        struct can_bittiming bittiming, data_bittiming;
        const struct can_bittiming_const *bittiming_const,
                *data_bittiming_const;
+       const u16 *termination_const;
+       unsigned int termination_const_cnt;
+       u16 termination;
        struct can_clock clock;
 
        enum can_state state;
@@ -53,6 +56,7 @@ struct can_priv {
        int (*do_set_bittiming)(struct net_device *dev);
        int (*do_set_data_bittiming)(struct net_device *dev);
        int (*do_set_mode)(struct net_device *dev, enum can_mode mode);
+       int (*do_set_termination)(struct net_device *dev, u16 term);
        int (*do_get_state)(const struct net_device *dev,
                            enum can_state *state);
        int (*do_get_berr_counter)(const struct net_device *dev,
index 94ffe0c83ce72cf5e396a615fe039bec6fe468d4..7414771926fb365189750580ed114708a1c55c21 100644 (file)
@@ -127,9 +127,14 @@ enum {
        IFLA_CAN_BERR_COUNTER,
        IFLA_CAN_DATA_BITTIMING,
        IFLA_CAN_DATA_BITTIMING_CONST,
+       IFLA_CAN_TERMINATION,
+       IFLA_CAN_TERMINATION_CONST,
        __IFLA_CAN_MAX
 };
 
 #define IFLA_CAN_MAX   (__IFLA_CAN_MAX - 1)
 
+/* u16 termination range: 1..65535 Ohms */
+#define CAN_TERMINATION_DISABLED 0
+
 #endif /* !_UAPI_CAN_NETLINK_H */