can: kvaser_usb_leaf: Fix improved state not being reported
[ 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: 080f40a6fa
("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>
This commit is contained in:
parent
60ad08be78
commit
9208795770
@ -104,6 +104,9 @@ struct kvaser_usb_net_priv {
|
|||||||
struct can_priv can;
|
struct can_priv can;
|
||||||
struct can_berr_counter bec;
|
struct can_berr_counter bec;
|
||||||
|
|
||||||
|
/* subdriver-specific data */
|
||||||
|
void *sub_priv;
|
||||||
|
|
||||||
struct kvaser_usb *dev;
|
struct kvaser_usb *dev;
|
||||||
struct net_device *netdev;
|
struct net_device *netdev;
|
||||||
int channel;
|
int channel;
|
||||||
@ -125,6 +128,8 @@ struct kvaser_usb_net_priv {
|
|||||||
*
|
*
|
||||||
* @dev_setup_endpoints: setup USB in and out endpoints
|
* @dev_setup_endpoints: setup USB in and out endpoints
|
||||||
* @dev_init_card: initialize card
|
* @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_info: get software info
|
||||||
* @dev_get_software_details: get software details
|
* @dev_get_software_details: get software details
|
||||||
* @dev_get_card_info: get card info
|
* @dev_get_card_info: get card info
|
||||||
@ -146,6 +151,8 @@ struct kvaser_usb_dev_ops {
|
|||||||
struct can_berr_counter *bec);
|
struct can_berr_counter *bec);
|
||||||
int (*dev_setup_endpoints)(struct kvaser_usb *dev);
|
int (*dev_setup_endpoints)(struct kvaser_usb *dev);
|
||||||
int (*dev_init_card)(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_info)(struct kvaser_usb *dev);
|
||||||
int (*dev_get_software_details)(struct kvaser_usb *dev);
|
int (*dev_get_software_details)(struct kvaser_usb *dev);
|
||||||
int (*dev_get_card_info)(struct kvaser_usb *dev);
|
int (*dev_get_card_info)(struct kvaser_usb *dev);
|
||||||
|
@ -645,6 +645,7 @@ static const struct net_device_ops kvaser_usb_netdev_ops = {
|
|||||||
|
|
||||||
static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
|
static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
|
||||||
{
|
{
|
||||||
|
const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < dev->nchannels; 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])
|
if (!dev->nets[i])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (ops->dev_remove_channel)
|
||||||
|
ops->dev_remove_channel(dev->nets[i]);
|
||||||
|
|
||||||
free_candev(dev->nets[i]->netdev);
|
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;
|
dev->nets[channel] = priv;
|
||||||
|
|
||||||
|
if (ops->dev_init_channel) {
|
||||||
|
err = ops->dev_init_channel(priv);
|
||||||
|
if (err)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
err = register_candev(netdev);
|
err = register_candev(netdev);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&dev->intf->dev, "Failed to register CAN device\n");
|
dev_err(&dev->intf->dev, "Failed to register CAN device\n");
|
||||||
free_candev(netdev);
|
goto err;
|
||||||
dev->nets[channel] = NULL;
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
netdev_dbg(netdev, "device registered\n");
|
netdev_dbg(netdev, "device registered\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
free_candev(netdev);
|
||||||
|
dev->nets[channel] = NULL;
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvaser_usb_probe(struct usb_interface *intf,
|
static int kvaser_usb_probe(struct usb_interface *intf,
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#include <linux/can.h>
|
#include <linux/can.h>
|
||||||
#include <linux/can/dev.h>
|
#include <linux/can/dev.h>
|
||||||
@ -55,6 +56,7 @@
|
|||||||
#define CMD_RX_EXT_MESSAGE 14
|
#define CMD_RX_EXT_MESSAGE 14
|
||||||
#define CMD_TX_EXT_MESSAGE 15
|
#define CMD_TX_EXT_MESSAGE 15
|
||||||
#define CMD_SET_BUS_PARAMS 16
|
#define CMD_SET_BUS_PARAMS 16
|
||||||
|
#define CMD_GET_CHIP_STATE 19
|
||||||
#define CMD_CHIP_STATE_EVENT 20
|
#define CMD_CHIP_STATE_EVENT 20
|
||||||
#define CMD_SET_CTRL_MODE 21
|
#define CMD_SET_CTRL_MODE 21
|
||||||
#define CMD_RESET_CHIP 24
|
#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 = {
|
static const struct can_bittiming_const kvaser_usb_leaf_m16c_bittiming_const = {
|
||||||
.name = "kvaser_usb_ucii",
|
.name = "kvaser_usb_ucii",
|
||||||
.tseg1_min = 4,
|
.tseg1_min = 4,
|
||||||
@ -947,6 +955,16 @@ static int kvaser_usb_leaf_simple_cmd_async(struct kvaser_usb_net_priv *priv,
|
|||||||
return err;
|
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
|
static void
|
||||||
kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
|
kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
|
||||||
const struct kvaser_usb_err_summary *es,
|
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 sk_buff *skb;
|
||||||
struct net_device_stats *stats;
|
struct net_device_stats *stats;
|
||||||
struct kvaser_usb_net_priv *priv;
|
struct kvaser_usb_net_priv *priv;
|
||||||
|
struct kvaser_usb_net_leaf_priv *leaf;
|
||||||
enum can_state old_state, new_state;
|
enum can_state old_state, new_state;
|
||||||
|
|
||||||
if (es->channel >= dev->nchannels) {
|
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];
|
priv = dev->nets[es->channel];
|
||||||
|
leaf = priv->sub_priv;
|
||||||
stats = &priv->netdev->stats;
|
stats = &priv->netdev->stats;
|
||||||
|
|
||||||
/* Update all of the CAN interface's state and error counters before
|
/* 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);
|
kvaser_usb_leaf_rx_error_update_can_state(priv, es, &tmp_cf);
|
||||||
new_state = priv->can.state;
|
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);
|
skb = alloc_can_err_skb(priv->netdev, &cf);
|
||||||
if (!skb) {
|
if (!skb) {
|
||||||
stats->rx_dropped++;
|
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)
|
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;
|
int err;
|
||||||
|
|
||||||
reinit_completion(&priv->stop_comp);
|
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,
|
err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_STOP_CHIP,
|
||||||
priv->channel);
|
priv->channel);
|
||||||
if (err)
|
if (err)
|
||||||
@ -1627,6 +1658,31 @@ static int kvaser_usb_leaf_init_card(struct kvaser_usb *dev)
|
|||||||
return 0;
|
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)
|
static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev)
|
||||||
{
|
{
|
||||||
struct kvaser_usb_net_priv *priv = netdev_priv(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_get_berr_counter = kvaser_usb_leaf_get_berr_counter,
|
||||||
.dev_setup_endpoints = kvaser_usb_leaf_setup_endpoints,
|
.dev_setup_endpoints = kvaser_usb_leaf_setup_endpoints,
|
||||||
.dev_init_card = kvaser_usb_leaf_init_card,
|
.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_info = kvaser_usb_leaf_get_software_info,
|
||||||
.dev_get_software_details = NULL,
|
.dev_get_software_details = NULL,
|
||||||
.dev_get_card_info = kvaser_usb_leaf_get_card_info,
|
.dev_get_card_info = kvaser_usb_leaf_get_card_info,
|
||||||
|
Loading…
Reference in New Issue
Block a user