]> git.baikalelectronics.ru Git - kernel.git/commitdiff
can: kvaser_usb_leaf: Fix improved state not being reported
authorAnssi Hannula <anssi.hannula@bitwise.fi>
Mon, 10 Oct 2022 18:52:32 +0000 (20:52 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 18 Jan 2023 10:41:00 +0000 (11:41 +0100)
[ Upstream commit 8d21f5927ae604881f98587fabf6753f88730968 ]

The tested 0bfd:0017 Kvaser Memorator Professional HS/HS FW 2.0.50 and
0bfd:0124 Kvaser Mini PCI Express 2xHS FW 4.18.778 do not seem to send
any unsolicited events when error counters decrease or when the device
transitions from ERROR_PASSIVE to ERROR_ACTIVE (or WARNING).

This causes the interface to e.g. indefinitely stay in the ERROR_PASSIVE
state.

Fix that by asking for chip state (inc. counters) event every 0.5 secs
when error counters are non-zero.

Since there are non-error-counter devices, also always poll in
ERROR_PASSIVE even if the counters show zero.

Fixes: d9f21bf91c45 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices")
Tested-by: Jimmy Assarsson <extja@kvaser.com>
Signed-off-by: Anssi Hannula <anssi.hannula@bitwise.fi>
Signed-off-by: Jimmy Assarsson <extja@kvaser.com>
Link: https://lore.kernel.org/all/20221010185237.319219-7-extja@kvaser.com
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/can/usb/kvaser_usb/kvaser_usb.h
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c

index 62958f04a2f20ea613b2d0754937b7b3718be6de..1f4583f1dae2a0bf33cf417f0912c3b989c6d696 100644 (file)
@@ -104,6 +104,9 @@ struct kvaser_usb_net_priv {
        struct can_priv can;
        struct can_berr_counter bec;
 
+       /* subdriver-specific data */
+       void *sub_priv;
+
        struct kvaser_usb *dev;
        struct net_device *netdev;
        int channel;
@@ -125,6 +128,8 @@ struct kvaser_usb_net_priv {
  *
  * @dev_setup_endpoints:       setup USB in and out endpoints
  * @dev_init_card:             initialize card
+ * @dev_init_channel:          initialize channel
+ * @dev_remove_channel:                uninitialize channel
  * @dev_get_software_info:     get software info
  * @dev_get_software_details:  get software details
  * @dev_get_card_info:         get card info
@@ -146,6 +151,8 @@ struct kvaser_usb_dev_ops {
                                    struct can_berr_counter *bec);
        int (*dev_setup_endpoints)(struct kvaser_usb *dev);
        int (*dev_init_card)(struct kvaser_usb *dev);
+       int (*dev_init_channel)(struct kvaser_usb_net_priv *priv);
+       void (*dev_remove_channel)(struct kvaser_usb_net_priv *priv);
        int (*dev_get_software_info)(struct kvaser_usb *dev);
        int (*dev_get_software_details)(struct kvaser_usb *dev);
        int (*dev_get_card_info)(struct kvaser_usb *dev);
index 7491f85e85b304bcd8904b974fbd8ce899f714bb..2c816d8929daaa8a277a147bf0a08bbb37daa53a 100644 (file)
@@ -645,6 +645,7 @@ static const struct net_device_ops kvaser_usb_netdev_ops = {
 
 static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
 {
+       const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops;
        int i;
 
        for (i = 0; i < dev->nchannels; i++) {
@@ -660,6 +661,9 @@ static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
                if (!dev->nets[i])
                        continue;
 
+               if (ops->dev_remove_channel)
+                       ops->dev_remove_channel(dev->nets[i]);
+
                free_candev(dev->nets[i]->netdev);
        }
 }
@@ -727,17 +731,26 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel)
 
        dev->nets[channel] = priv;
 
+       if (ops->dev_init_channel) {
+               err = ops->dev_init_channel(priv);
+               if (err)
+                       goto err;
+       }
+
        err = register_candev(netdev);
        if (err) {
                dev_err(&dev->intf->dev, "Failed to register CAN device\n");
-               free_candev(netdev);
-               dev->nets[channel] = NULL;
-               return err;
+               goto err;
        }
 
        netdev_dbg(netdev, "device registered\n");
 
        return 0;
+
+err:
+       free_candev(netdev);
+       dev->nets[channel] = NULL;
+       return err;
 }
 
 static int kvaser_usb_probe(struct usb_interface *intf,
index b43631eaccf1babde87101b489972d5f6859f9f5..6d45ae6f2a08c3a7a19f8eeacf8cc0000c144b19 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/usb.h>
+#include <linux/workqueue.h>
 
 #include <linux/can.h>
 #include <linux/can/dev.h>
@@ -55,6 +56,7 @@
 #define CMD_RX_EXT_MESSAGE             14
 #define CMD_TX_EXT_MESSAGE             15
 #define CMD_SET_BUS_PARAMS             16
+#define CMD_GET_CHIP_STATE             19
 #define CMD_CHIP_STATE_EVENT           20
 #define CMD_SET_CTRL_MODE              21
 #define CMD_RESET_CHIP                 24
@@ -420,6 +422,12 @@ struct kvaser_usb_err_summary {
        };
 };
 
+struct kvaser_usb_net_leaf_priv {
+       struct kvaser_usb_net_priv *net;
+
+       struct delayed_work chip_state_req_work;
+};
+
 static const struct can_bittiming_const kvaser_usb_leaf_m16c_bittiming_const = {
        .name = "kvaser_usb_ucii",
        .tseg1_min = 4,
@@ -947,6 +955,16 @@ static int kvaser_usb_leaf_simple_cmd_async(struct kvaser_usb_net_priv *priv,
        return err;
 }
 
+static void kvaser_usb_leaf_chip_state_req_work(struct work_struct *work)
+{
+       struct kvaser_usb_net_leaf_priv *leaf =
+               container_of(work, struct kvaser_usb_net_leaf_priv,
+                            chip_state_req_work.work);
+       struct kvaser_usb_net_priv *priv = leaf->net;
+
+       kvaser_usb_leaf_simple_cmd_async(priv, CMD_GET_CHIP_STATE);
+}
+
 static void
 kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
                                        const struct kvaser_usb_err_summary *es,
@@ -1018,6 +1036,7 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev,
        struct sk_buff *skb;
        struct net_device_stats *stats;
        struct kvaser_usb_net_priv *priv;
+       struct kvaser_usb_net_leaf_priv *leaf;
        enum can_state old_state, new_state;
 
        if (es->channel >= dev->nchannels) {
@@ -1027,6 +1046,7 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev,
        }
 
        priv = dev->nets[es->channel];
+       leaf = priv->sub_priv;
        stats = &priv->netdev->stats;
 
        /* Update all of the CAN interface's state and error counters before
@@ -1043,6 +1063,14 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev,
        kvaser_usb_leaf_rx_error_update_can_state(priv, es, &tmp_cf);
        new_state = priv->can.state;
 
+       /* If there are errors, request status updates periodically as we do
+        * not get automatic notifications of improved state.
+        */
+       if (new_state < CAN_STATE_BUS_OFF &&
+           (es->rxerr || es->txerr || new_state == CAN_STATE_ERROR_PASSIVE))
+               schedule_delayed_work(&leaf->chip_state_req_work,
+                                     msecs_to_jiffies(500));
+
        skb = alloc_can_err_skb(priv->netdev, &cf);
        if (!skb) {
                stats->rx_dropped++;
@@ -1577,10 +1605,13 @@ static int kvaser_usb_leaf_start_chip(struct kvaser_usb_net_priv *priv)
 
 static int kvaser_usb_leaf_stop_chip(struct kvaser_usb_net_priv *priv)
 {
+       struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv;
        int err;
 
        reinit_completion(&priv->stop_comp);
 
+       cancel_delayed_work(&leaf->chip_state_req_work);
+
        err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_STOP_CHIP,
                                              priv->channel);
        if (err)
@@ -1627,6 +1658,31 @@ static int kvaser_usb_leaf_init_card(struct kvaser_usb *dev)
        return 0;
 }
 
+static int kvaser_usb_leaf_init_channel(struct kvaser_usb_net_priv *priv)
+{
+       struct kvaser_usb_net_leaf_priv *leaf;
+
+       leaf = devm_kzalloc(&priv->dev->intf->dev, sizeof(*leaf), GFP_KERNEL);
+       if (!leaf)
+               return -ENOMEM;
+
+       leaf->net = priv;
+       INIT_DELAYED_WORK(&leaf->chip_state_req_work,
+                         kvaser_usb_leaf_chip_state_req_work);
+
+       priv->sub_priv = leaf;
+
+       return 0;
+}
+
+static void kvaser_usb_leaf_remove_channel(struct kvaser_usb_net_priv *priv)
+{
+       struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv;
+
+       if (leaf)
+               cancel_delayed_work_sync(&leaf->chip_state_req_work);
+}
+
 static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev)
 {
        struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
@@ -1724,6 +1780,8 @@ const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = {
        .dev_get_berr_counter = kvaser_usb_leaf_get_berr_counter,
        .dev_setup_endpoints = kvaser_usb_leaf_setup_endpoints,
        .dev_init_card = kvaser_usb_leaf_init_card,
+       .dev_init_channel = kvaser_usb_leaf_init_channel,
+       .dev_remove_channel = kvaser_usb_leaf_remove_channel,
        .dev_get_software_info = kvaser_usb_leaf_get_software_info,
        .dev_get_software_details = NULL,
        .dev_get_card_info = kvaser_usb_leaf_get_card_info,