Merge patch series "can: kvaser_usb: Fixes and improvements"
Jimmy Assarsson <extja@kvaser.com> says:
Split v4 series, since it got rejected [1]. This part only contains
non-critical fixes and improvements.
Note: This series depend on changes in [2].
Changes in v5:
- Split v4 series, since it got rejected [1].
This part only contains non-critical fixes and improvements.
Changes in v4: https://lore.kernel.org/all/20220903182344.139-1-extja@kvaser.com
- Add Tested-by: Anssi Hannula to
[PATCH v4 04/15] can: kvaser_usb: kvaser_usb_leaf: Get capabilities from device
- Update commit message in
[PATCH v4 04/15] can: kvaser_usb: kvaser_usb_leaf: Get capabilities from device
Changes in v3: https://lore.kernel.org/all/20220901122729.271-1-extja@kvaser.com
- Rebase on top of commit
1d5eeda23f
("can: kvaser_usb: advertise timestamping capabilities and add ioctl support")
- Add Tested-by: Anssi Hannula
- Add stable@vger.kernel.org to CC.
- Add my S-o-b to all patches
- Fix regression introduced in
[PATCH v2 04/15] can: kvaser_usb: kvaser_usb_leaf: Get capabilities from device
found by Anssi Hannula
https://lore.kernel.org/all/b25bc059-d776-146d-0b3c-41aecf4bd9f8@bitwise.fi
v2: https://lore.kernel.org/all/20220708115709.232815-1-extja@kvaser.com
v1: https://lore.kernel.org/all/20220516134748.3724796-1-anssi.hannula@bitwise.fi
[1] https://lore.kernel.org/all/20220920122246.00dbe946@kernel.org
[2] https://lore.kernel.org/all/20221010150829.199676-1-extja@kvaser.com
Link: https://lore.kernel.org/all/20221010185237.319219-1-extja@kvaser.com
[mkl: move "1/1 can: kvaser_usb: Fix possible completions during init_completion" to linux-can tree]
[mkl: improve change log, update links]
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
commit
13441ed666
|
@ -76,6 +76,14 @@ struct kvaser_usb_tx_urb_context {
|
|||
u32 echo_index;
|
||||
};
|
||||
|
||||
struct kvaser_usb_busparams {
|
||||
__le32 bitrate;
|
||||
u8 tseg1;
|
||||
u8 tseg2;
|
||||
u8 sjw;
|
||||
u8 nsamples;
|
||||
} __packed;
|
||||
|
||||
struct kvaser_usb {
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *intf;
|
||||
|
@ -104,13 +112,19 @@ 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;
|
||||
|
||||
struct completion start_comp, stop_comp, flush_comp;
|
||||
struct completion start_comp, stop_comp, flush_comp,
|
||||
get_busparams_comp;
|
||||
struct usb_anchor tx_submitted;
|
||||
|
||||
struct kvaser_usb_busparams busparams_nominal, busparams_data;
|
||||
|
||||
spinlock_t tx_contexts_lock; /* lock for active_tx_contexts */
|
||||
int active_tx_contexts;
|
||||
struct kvaser_usb_tx_urb_context tx_contexts[];
|
||||
|
@ -120,11 +134,15 @@ struct kvaser_usb_net_priv {
|
|||
* struct kvaser_usb_dev_ops - Device specific functions
|
||||
* @dev_set_mode: used for can.do_set_mode
|
||||
* @dev_set_bittiming: used for can.do_set_bittiming
|
||||
* @dev_get_busparams: readback arbitration busparams
|
||||
* @dev_set_data_bittiming: used for can.do_set_data_bittiming
|
||||
* @dev_get_data_busparams: readback data busparams
|
||||
* @dev_get_berr_counter: used for can.do_get_berr_counter
|
||||
*
|
||||
* @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
|
||||
|
@ -140,12 +158,18 @@ struct kvaser_usb_net_priv {
|
|||
*/
|
||||
struct kvaser_usb_dev_ops {
|
||||
int (*dev_set_mode)(struct net_device *netdev, enum can_mode mode);
|
||||
int (*dev_set_bittiming)(struct net_device *netdev);
|
||||
int (*dev_set_data_bittiming)(struct net_device *netdev);
|
||||
int (*dev_set_bittiming)(const struct net_device *netdev,
|
||||
const struct kvaser_usb_busparams *busparams);
|
||||
int (*dev_get_busparams)(struct kvaser_usb_net_priv *priv);
|
||||
int (*dev_set_data_bittiming)(const struct net_device *netdev,
|
||||
const struct kvaser_usb_busparams *busparams);
|
||||
int (*dev_get_data_busparams)(struct kvaser_usb_net_priv *priv);
|
||||
int (*dev_get_berr_counter)(const struct net_device *netdev,
|
||||
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);
|
||||
|
|
|
@ -440,10 +440,6 @@ static int kvaser_usb_open(struct net_device *netdev)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = kvaser_usb_setup_rx_urbs(dev);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = ops->dev_set_opt_mode(priv);
|
||||
if (err)
|
||||
goto error;
|
||||
|
@ -534,6 +530,93 @@ static int kvaser_usb_close(struct net_device *netdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_set_bittiming(struct net_device *netdev)
|
||||
{
|
||||
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops;
|
||||
struct can_bittiming *bt = &priv->can.bittiming;
|
||||
|
||||
struct kvaser_usb_busparams busparams;
|
||||
int tseg1 = bt->prop_seg + bt->phase_seg1;
|
||||
int tseg2 = bt->phase_seg2;
|
||||
int sjw = bt->sjw;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
busparams.bitrate = cpu_to_le32(bt->bitrate);
|
||||
busparams.sjw = (u8)sjw;
|
||||
busparams.tseg1 = (u8)tseg1;
|
||||
busparams.tseg2 = (u8)tseg2;
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
|
||||
busparams.nsamples = 3;
|
||||
else
|
||||
busparams.nsamples = 1;
|
||||
|
||||
err = ops->dev_set_bittiming(netdev, &busparams);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kvaser_usb_setup_rx_urbs(priv->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ops->dev_get_busparams(priv);
|
||||
if (err) {
|
||||
/* Treat EOPNOTSUPP as success */
|
||||
if (err == -EOPNOTSUPP)
|
||||
err = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (memcmp(&busparams, &priv->busparams_nominal,
|
||||
sizeof(priv->busparams_nominal)) != 0)
|
||||
err = -EINVAL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int kvaser_usb_set_data_bittiming(struct net_device *netdev)
|
||||
{
|
||||
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops;
|
||||
struct can_bittiming *dbt = &priv->can.data_bittiming;
|
||||
|
||||
struct kvaser_usb_busparams busparams;
|
||||
int tseg1 = dbt->prop_seg + dbt->phase_seg1;
|
||||
int tseg2 = dbt->phase_seg2;
|
||||
int sjw = dbt->sjw;
|
||||
int err;
|
||||
|
||||
if (!ops->dev_set_data_bittiming ||
|
||||
!ops->dev_get_data_busparams)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
busparams.bitrate = cpu_to_le32(dbt->bitrate);
|
||||
busparams.sjw = (u8)sjw;
|
||||
busparams.tseg1 = (u8)tseg1;
|
||||
busparams.tseg2 = (u8)tseg2;
|
||||
busparams.nsamples = 1;
|
||||
|
||||
err = ops->dev_set_data_bittiming(netdev, &busparams);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kvaser_usb_setup_rx_urbs(priv->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ops->dev_get_data_busparams(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (memcmp(&busparams, &priv->busparams_data,
|
||||
sizeof(priv->busparams_data)) != 0)
|
||||
err = -EINVAL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void kvaser_usb_write_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct kvaser_usb_tx_urb_context *context = urb->context;
|
||||
|
@ -684,6 +767,7 @@ static const struct ethtool_ops kvaser_usb_ethtool_ops_hwts = {
|
|||
|
||||
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++) {
|
||||
|
@ -699,6 +783,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);
|
||||
}
|
||||
}
|
||||
|
@ -730,6 +817,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel)
|
|||
init_completion(&priv->start_comp);
|
||||
init_completion(&priv->stop_comp);
|
||||
init_completion(&priv->flush_comp);
|
||||
init_completion(&priv->get_busparams_comp);
|
||||
priv->can.ctrlmode_supported = 0;
|
||||
|
||||
priv->dev = dev;
|
||||
|
@ -742,7 +830,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel)
|
|||
priv->can.state = CAN_STATE_STOPPED;
|
||||
priv->can.clock.freq = dev->cfg->clock.freq;
|
||||
priv->can.bittiming_const = dev->cfg->bittiming_const;
|
||||
priv->can.do_set_bittiming = ops->dev_set_bittiming;
|
||||
priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
|
||||
priv->can.do_set_mode = ops->dev_set_mode;
|
||||
if ((driver_info->quirks & KVASER_USB_QUIRK_HAS_TXRX_ERRORS) ||
|
||||
(priv->dev->card_data.capabilities & KVASER_USB_CAP_BERR_CAP))
|
||||
|
@ -754,7 +842,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel)
|
|||
|
||||
if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) {
|
||||
priv->can.data_bittiming_const = dev->cfg->data_bittiming_const;
|
||||
priv->can.do_set_data_bittiming = ops->dev_set_data_bittiming;
|
||||
priv->can.do_set_data_bittiming = kvaser_usb_set_data_bittiming;
|
||||
}
|
||||
|
||||
netdev->flags |= IFF_ECHO;
|
||||
|
@ -772,17 +860,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,
|
||||
|
|
|
@ -45,6 +45,8 @@ static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_rt;
|
|||
|
||||
/* Minihydra command IDs */
|
||||
#define CMD_SET_BUSPARAMS_REQ 16
|
||||
#define CMD_GET_BUSPARAMS_REQ 17
|
||||
#define CMD_GET_BUSPARAMS_RESP 18
|
||||
#define CMD_GET_CHIP_STATE_REQ 19
|
||||
#define CMD_CHIP_STATE_EVENT 20
|
||||
#define CMD_SET_DRIVERMODE_REQ 21
|
||||
|
@ -196,21 +198,26 @@ struct kvaser_cmd_chip_state_event {
|
|||
#define KVASER_USB_HYDRA_BUS_MODE_CANFD_ISO 0x01
|
||||
#define KVASER_USB_HYDRA_BUS_MODE_NONISO 0x02
|
||||
struct kvaser_cmd_set_busparams {
|
||||
__le32 bitrate;
|
||||
u8 tseg1;
|
||||
u8 tseg2;
|
||||
u8 sjw;
|
||||
u8 nsamples;
|
||||
struct kvaser_usb_busparams busparams_nominal;
|
||||
u8 reserved0[4];
|
||||
__le32 bitrate_d;
|
||||
u8 tseg1_d;
|
||||
u8 tseg2_d;
|
||||
u8 sjw_d;
|
||||
u8 nsamples_d;
|
||||
struct kvaser_usb_busparams busparams_data;
|
||||
u8 canfd_mode;
|
||||
u8 reserved1[7];
|
||||
} __packed;
|
||||
|
||||
/* Busparam type */
|
||||
#define KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN 0x00
|
||||
#define KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD 0x01
|
||||
struct kvaser_cmd_get_busparams_req {
|
||||
u8 type;
|
||||
u8 reserved[27];
|
||||
} __packed;
|
||||
|
||||
struct kvaser_cmd_get_busparams_res {
|
||||
struct kvaser_usb_busparams busparams;
|
||||
u8 reserved[20];
|
||||
} __packed;
|
||||
|
||||
/* Ctrl modes */
|
||||
#define KVASER_USB_HYDRA_CTRLMODE_NORMAL 0x01
|
||||
#define KVASER_USB_HYDRA_CTRLMODE_LISTEN 0x02
|
||||
|
@ -281,6 +288,8 @@ struct kvaser_cmd {
|
|||
struct kvaser_cmd_error_event error_event;
|
||||
|
||||
struct kvaser_cmd_set_busparams set_busparams_req;
|
||||
struct kvaser_cmd_get_busparams_req get_busparams_req;
|
||||
struct kvaser_cmd_get_busparams_res get_busparams_res;
|
||||
|
||||
struct kvaser_cmd_chip_state_event chip_state_event;
|
||||
|
||||
|
@ -363,6 +372,10 @@ struct kvaser_cmd_ext {
|
|||
} __packed;
|
||||
} __packed;
|
||||
|
||||
struct kvaser_usb_net_hydra_priv {
|
||||
int pending_get_busparams_type;
|
||||
};
|
||||
|
||||
static const struct can_bittiming_const kvaser_usb_hydra_kcan_bittiming_c = {
|
||||
.name = "kvaser_usb_kcan",
|
||||
.tseg1_min = 1,
|
||||
|
@ -840,6 +853,39 @@ static void kvaser_usb_hydra_flush_queue_reply(const struct kvaser_usb *dev,
|
|||
complete(&priv->flush_comp);
|
||||
}
|
||||
|
||||
static void kvaser_usb_hydra_get_busparams_reply(const struct kvaser_usb *dev,
|
||||
const struct kvaser_cmd *cmd)
|
||||
{
|
||||
struct kvaser_usb_net_priv *priv;
|
||||
struct kvaser_usb_net_hydra_priv *hydra;
|
||||
|
||||
priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd);
|
||||
if (!priv)
|
||||
return;
|
||||
|
||||
hydra = priv->sub_priv;
|
||||
if (!hydra)
|
||||
return;
|
||||
|
||||
switch (hydra->pending_get_busparams_type) {
|
||||
case KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN:
|
||||
memcpy(&priv->busparams_nominal, &cmd->get_busparams_res.busparams,
|
||||
sizeof(priv->busparams_nominal));
|
||||
break;
|
||||
case KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD:
|
||||
memcpy(&priv->busparams_data, &cmd->get_busparams_res.busparams,
|
||||
sizeof(priv->busparams_nominal));
|
||||
break;
|
||||
default:
|
||||
dev_warn(&dev->intf->dev, "Unknown get_busparams_type %d\n",
|
||||
hydra->pending_get_busparams_type);
|
||||
break;
|
||||
}
|
||||
hydra->pending_get_busparams_type = -1;
|
||||
|
||||
complete(&priv->get_busparams_comp);
|
||||
}
|
||||
|
||||
static void
|
||||
kvaser_usb_hydra_bus_status_to_can_state(const struct kvaser_usb_net_priv *priv,
|
||||
u8 bus_status,
|
||||
|
@ -1326,6 +1372,10 @@ static void kvaser_usb_hydra_handle_cmd_std(const struct kvaser_usb *dev,
|
|||
kvaser_usb_hydra_state_event(dev, cmd);
|
||||
break;
|
||||
|
||||
case CMD_GET_BUSPARAMS_RESP:
|
||||
kvaser_usb_hydra_get_busparams_reply(dev, cmd);
|
||||
break;
|
||||
|
||||
case CMD_ERROR_EVENT:
|
||||
kvaser_usb_hydra_error_event(dev, cmd);
|
||||
break;
|
||||
|
@ -1522,15 +1572,58 @@ static int kvaser_usb_hydra_set_mode(struct net_device *netdev,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev)
|
||||
static int kvaser_usb_hydra_get_busparams(struct kvaser_usb_net_priv *priv,
|
||||
int busparams_type)
|
||||
{
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
struct kvaser_usb_net_hydra_priv *hydra = priv->sub_priv;
|
||||
struct kvaser_cmd *cmd;
|
||||
int err;
|
||||
|
||||
if (!hydra)
|
||||
return -EINVAL;
|
||||
|
||||
cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->header.cmd_no = CMD_GET_BUSPARAMS_REQ;
|
||||
kvaser_usb_hydra_set_cmd_dest_he
|
||||
(cmd, dev->card_data.hydra.channel_to_he[priv->channel]);
|
||||
kvaser_usb_hydra_set_cmd_transid
|
||||
(cmd, kvaser_usb_hydra_get_next_transid(dev));
|
||||
cmd->get_busparams_req.type = busparams_type;
|
||||
hydra->pending_get_busparams_type = busparams_type;
|
||||
|
||||
reinit_completion(&priv->get_busparams_comp);
|
||||
|
||||
err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!wait_for_completion_timeout(&priv->get_busparams_comp,
|
||||
msecs_to_jiffies(KVASER_USB_TIMEOUT)))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int kvaser_usb_hydra_get_nominal_busparams(struct kvaser_usb_net_priv *priv)
|
||||
{
|
||||
return kvaser_usb_hydra_get_busparams(priv, KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN);
|
||||
}
|
||||
|
||||
static int kvaser_usb_hydra_get_data_busparams(struct kvaser_usb_net_priv *priv)
|
||||
{
|
||||
return kvaser_usb_hydra_get_busparams(priv, KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD);
|
||||
}
|
||||
|
||||
static int kvaser_usb_hydra_set_bittiming(const struct net_device *netdev,
|
||||
const struct kvaser_usb_busparams *busparams)
|
||||
{
|
||||
struct kvaser_cmd *cmd;
|
||||
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
|
||||
struct can_bittiming *bt = &priv->can.bittiming;
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
int tseg1 = bt->prop_seg + bt->phase_seg1;
|
||||
int tseg2 = bt->phase_seg2;
|
||||
int sjw = bt->sjw;
|
||||
int err;
|
||||
|
||||
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
||||
|
@ -1538,11 +1631,8 @@ static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev)
|
|||
return -ENOMEM;
|
||||
|
||||
cmd->header.cmd_no = CMD_SET_BUSPARAMS_REQ;
|
||||
cmd->set_busparams_req.bitrate = cpu_to_le32(bt->bitrate);
|
||||
cmd->set_busparams_req.sjw = (u8)sjw;
|
||||
cmd->set_busparams_req.tseg1 = (u8)tseg1;
|
||||
cmd->set_busparams_req.tseg2 = (u8)tseg2;
|
||||
cmd->set_busparams_req.nsamples = 1;
|
||||
memcpy(&cmd->set_busparams_req.busparams_nominal, busparams,
|
||||
sizeof(cmd->set_busparams_req.busparams_nominal));
|
||||
|
||||
kvaser_usb_hydra_set_cmd_dest_he
|
||||
(cmd, dev->card_data.hydra.channel_to_he[priv->channel]);
|
||||
|
@ -1556,15 +1646,12 @@ static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int kvaser_usb_hydra_set_data_bittiming(struct net_device *netdev)
|
||||
static int kvaser_usb_hydra_set_data_bittiming(const struct net_device *netdev,
|
||||
const struct kvaser_usb_busparams *busparams)
|
||||
{
|
||||
struct kvaser_cmd *cmd;
|
||||
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
|
||||
struct can_bittiming *dbt = &priv->can.data_bittiming;
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
int tseg1 = dbt->prop_seg + dbt->phase_seg1;
|
||||
int tseg2 = dbt->phase_seg2;
|
||||
int sjw = dbt->sjw;
|
||||
int err;
|
||||
|
||||
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
||||
|
@ -1572,11 +1659,8 @@ static int kvaser_usb_hydra_set_data_bittiming(struct net_device *netdev)
|
|||
return -ENOMEM;
|
||||
|
||||
cmd->header.cmd_no = CMD_SET_BUSPARAMS_FD_REQ;
|
||||
cmd->set_busparams_req.bitrate_d = cpu_to_le32(dbt->bitrate);
|
||||
cmd->set_busparams_req.sjw_d = (u8)sjw;
|
||||
cmd->set_busparams_req.tseg1_d = (u8)tseg1;
|
||||
cmd->set_busparams_req.tseg2_d = (u8)tseg2;
|
||||
cmd->set_busparams_req.nsamples_d = 1;
|
||||
memcpy(&cmd->set_busparams_req.busparams_data, busparams,
|
||||
sizeof(cmd->set_busparams_req.busparams_data));
|
||||
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)
|
||||
|
@ -1683,6 +1767,19 @@ static int kvaser_usb_hydra_init_card(struct kvaser_usb *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_hydra_init_channel(struct kvaser_usb_net_priv *priv)
|
||||
{
|
||||
struct kvaser_usb_net_hydra_priv *hydra;
|
||||
|
||||
hydra = devm_kzalloc(&priv->dev->intf->dev, sizeof(*hydra), GFP_KERNEL);
|
||||
if (!hydra)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->sub_priv = hydra;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_hydra_get_software_info(struct kvaser_usb *dev)
|
||||
{
|
||||
struct kvaser_cmd cmd;
|
||||
|
@ -2027,10 +2124,13 @@ kvaser_usb_hydra_frame_to_cmd(const struct kvaser_usb_net_priv *priv,
|
|||
const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops = {
|
||||
.dev_set_mode = kvaser_usb_hydra_set_mode,
|
||||
.dev_set_bittiming = kvaser_usb_hydra_set_bittiming,
|
||||
.dev_get_busparams = kvaser_usb_hydra_get_nominal_busparams,
|
||||
.dev_set_data_bittiming = kvaser_usb_hydra_set_data_bittiming,
|
||||
.dev_get_data_busparams = kvaser_usb_hydra_get_data_busparams,
|
||||
.dev_get_berr_counter = kvaser_usb_hydra_get_berr_counter,
|
||||
.dev_setup_endpoints = kvaser_usb_hydra_setup_endpoints,
|
||||
.dev_init_card = kvaser_usb_hydra_init_card,
|
||||
.dev_init_channel = kvaser_usb_hydra_init_channel,
|
||||
.dev_get_software_info = kvaser_usb_hydra_get_software_info,
|
||||
.dev_get_software_details = kvaser_usb_hydra_get_software_details,
|
||||
.dev_get_card_info = kvaser_usb_hydra_get_card_info,
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
|
@ -56,6 +57,9 @@
|
|||
#define CMD_RX_EXT_MESSAGE 14
|
||||
#define CMD_TX_EXT_MESSAGE 15
|
||||
#define CMD_SET_BUS_PARAMS 16
|
||||
#define CMD_GET_BUS_PARAMS 17
|
||||
#define CMD_GET_BUS_PARAMS_REPLY 18
|
||||
#define CMD_GET_CHIP_STATE 19
|
||||
#define CMD_CHIP_STATE_EVENT 20
|
||||
#define CMD_SET_CTRL_MODE 21
|
||||
#define CMD_RESET_CHIP 24
|
||||
|
@ -70,10 +74,13 @@
|
|||
#define CMD_GET_CARD_INFO_REPLY 35
|
||||
#define CMD_GET_SOFTWARE_INFO 38
|
||||
#define CMD_GET_SOFTWARE_INFO_REPLY 39
|
||||
#define CMD_ERROR_EVENT 45
|
||||
#define CMD_FLUSH_QUEUE 48
|
||||
#define CMD_TX_ACKNOWLEDGE 50
|
||||
#define CMD_CAN_ERROR_EVENT 51
|
||||
#define CMD_FLUSH_QUEUE_REPLY 68
|
||||
#define CMD_GET_CAPABILITIES_REQ 95
|
||||
#define CMD_GET_CAPABILITIES_RESP 96
|
||||
|
||||
#define CMD_LEAF_LOG_MESSAGE 106
|
||||
|
||||
|
@ -83,6 +90,8 @@
|
|||
#define KVASER_USB_LEAF_SWOPTION_FREQ_32_MHZ_CLK BIT(5)
|
||||
#define KVASER_USB_LEAF_SWOPTION_FREQ_24_MHZ_CLK BIT(6)
|
||||
|
||||
#define KVASER_USB_LEAF_SWOPTION_EXT_CAP BIT(12)
|
||||
|
||||
/* error factors */
|
||||
#define M16C_EF_ACKE BIT(0)
|
||||
#define M16C_EF_CRCE BIT(1)
|
||||
|
@ -157,11 +166,7 @@ struct usbcan_cmd_softinfo {
|
|||
struct kvaser_cmd_busparams {
|
||||
u8 tid;
|
||||
u8 channel;
|
||||
__le32 bitrate;
|
||||
u8 tseg1;
|
||||
u8 tseg2;
|
||||
u8 sjw;
|
||||
u8 no_samp;
|
||||
struct kvaser_usb_busparams busparams;
|
||||
} __packed;
|
||||
|
||||
struct kvaser_cmd_tx_can {
|
||||
|
@ -230,7 +235,7 @@ struct kvaser_cmd_tx_acknowledge_header {
|
|||
u8 tid;
|
||||
} __packed;
|
||||
|
||||
struct leaf_cmd_error_event {
|
||||
struct leaf_cmd_can_error_event {
|
||||
u8 tid;
|
||||
u8 flags;
|
||||
__le16 time[3];
|
||||
|
@ -242,7 +247,7 @@ struct leaf_cmd_error_event {
|
|||
u8 error_factor;
|
||||
} __packed;
|
||||
|
||||
struct usbcan_cmd_error_event {
|
||||
struct usbcan_cmd_can_error_event {
|
||||
u8 tid;
|
||||
u8 padding;
|
||||
u8 tx_errors_count_ch0;
|
||||
|
@ -254,6 +259,28 @@ struct usbcan_cmd_error_event {
|
|||
__le16 time;
|
||||
} __packed;
|
||||
|
||||
/* CMD_ERROR_EVENT error codes */
|
||||
#define KVASER_USB_LEAF_ERROR_EVENT_TX_QUEUE_FULL 0x8
|
||||
#define KVASER_USB_LEAF_ERROR_EVENT_PARAM 0x9
|
||||
|
||||
struct leaf_cmd_error_event {
|
||||
u8 tid;
|
||||
u8 error_code;
|
||||
__le16 timestamp[3];
|
||||
__le16 padding;
|
||||
__le16 info1;
|
||||
__le16 info2;
|
||||
} __packed;
|
||||
|
||||
struct usbcan_cmd_error_event {
|
||||
u8 tid;
|
||||
u8 error_code;
|
||||
__le16 info1;
|
||||
__le16 info2;
|
||||
__le16 timestamp;
|
||||
__le16 padding;
|
||||
} __packed;
|
||||
|
||||
struct kvaser_cmd_ctrl_mode {
|
||||
u8 tid;
|
||||
u8 channel;
|
||||
|
@ -278,6 +305,28 @@ struct leaf_cmd_log_message {
|
|||
u8 data[8];
|
||||
} __packed;
|
||||
|
||||
/* Sub commands for cap_req and cap_res */
|
||||
#define KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE 0x02
|
||||
#define KVASER_USB_LEAF_CAP_CMD_ERR_REPORT 0x05
|
||||
struct kvaser_cmd_cap_req {
|
||||
__le16 padding0;
|
||||
__le16 cap_cmd;
|
||||
__le16 padding1;
|
||||
__le16 channel;
|
||||
} __packed;
|
||||
|
||||
/* Status codes for cap_res */
|
||||
#define KVASER_USB_LEAF_CAP_STAT_OK 0x00
|
||||
#define KVASER_USB_LEAF_CAP_STAT_NOT_IMPL 0x01
|
||||
#define KVASER_USB_LEAF_CAP_STAT_UNAVAIL 0x02
|
||||
struct kvaser_cmd_cap_res {
|
||||
__le16 padding;
|
||||
__le16 cap_cmd;
|
||||
__le16 status;
|
||||
__le32 mask;
|
||||
__le32 value;
|
||||
} __packed;
|
||||
|
||||
struct kvaser_cmd {
|
||||
u8 len;
|
||||
u8 id;
|
||||
|
@ -293,14 +342,18 @@ struct kvaser_cmd {
|
|||
struct leaf_cmd_softinfo softinfo;
|
||||
struct leaf_cmd_rx_can rx_can;
|
||||
struct leaf_cmd_chip_state_event chip_state_event;
|
||||
struct leaf_cmd_error_event error_event;
|
||||
struct leaf_cmd_can_error_event can_error_event;
|
||||
struct leaf_cmd_log_message log_message;
|
||||
struct leaf_cmd_error_event error_event;
|
||||
struct kvaser_cmd_cap_req cap_req;
|
||||
struct kvaser_cmd_cap_res cap_res;
|
||||
} __packed leaf;
|
||||
|
||||
union {
|
||||
struct usbcan_cmd_softinfo softinfo;
|
||||
struct usbcan_cmd_rx_can rx_can;
|
||||
struct usbcan_cmd_chip_state_event chip_state_event;
|
||||
struct usbcan_cmd_can_error_event can_error_event;
|
||||
struct usbcan_cmd_error_event error_event;
|
||||
} __packed usbcan;
|
||||
|
||||
|
@ -323,7 +376,10 @@ static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = {
|
|||
[CMD_RX_EXT_MESSAGE] = kvaser_fsize(u.leaf.rx_can),
|
||||
[CMD_LEAF_LOG_MESSAGE] = kvaser_fsize(u.leaf.log_message),
|
||||
[CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.leaf.chip_state_event),
|
||||
[CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event),
|
||||
[CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.can_error_event),
|
||||
[CMD_GET_CAPABILITIES_RESP] = kvaser_fsize(u.leaf.cap_res),
|
||||
[CMD_GET_BUS_PARAMS_REPLY] = kvaser_fsize(u.busparams),
|
||||
[CMD_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event),
|
||||
/* ignored events: */
|
||||
[CMD_FLUSH_QUEUE_REPLY] = CMD_SIZE_ANY,
|
||||
};
|
||||
|
@ -337,7 +393,8 @@ static const u8 kvaser_usb_leaf_cmd_sizes_usbcan[] = {
|
|||
[CMD_RX_STD_MESSAGE] = kvaser_fsize(u.usbcan.rx_can),
|
||||
[CMD_RX_EXT_MESSAGE] = kvaser_fsize(u.usbcan.rx_can),
|
||||
[CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.usbcan.chip_state_event),
|
||||
[CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.usbcan.error_event),
|
||||
[CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.usbcan.can_error_event),
|
||||
[CMD_ERROR_EVENT] = kvaser_fsize(u.usbcan.error_event),
|
||||
/* ignored events: */
|
||||
[CMD_USBCAN_CLOCK_OVERFLOW_EVENT] = CMD_SIZE_ANY,
|
||||
};
|
||||
|
@ -365,6 +422,15 @@ struct kvaser_usb_err_summary {
|
|||
};
|
||||
};
|
||||
|
||||
struct kvaser_usb_net_leaf_priv {
|
||||
struct kvaser_usb_net_priv *net;
|
||||
|
||||
struct delayed_work chip_state_req_work;
|
||||
|
||||
/* started but not reported as bus-on yet */
|
||||
bool joining_bus;
|
||||
};
|
||||
|
||||
static const struct can_bittiming_const kvaser_usb_leaf_m16c_bittiming_const = {
|
||||
.name = "kvaser_usb_ucii",
|
||||
.tseg1_min = 4,
|
||||
|
@ -606,6 +672,9 @@ static void kvaser_usb_leaf_get_software_info_leaf(struct kvaser_usb *dev,
|
|||
dev->fw_version = le32_to_cpu(softinfo->fw_version);
|
||||
dev->max_tx_urbs = le16_to_cpu(softinfo->max_outstanding_tx);
|
||||
|
||||
if (sw_options & KVASER_USB_LEAF_SWOPTION_EXT_CAP)
|
||||
dev->card_data.capabilities |= KVASER_USB_CAP_EXT_CAP;
|
||||
|
||||
if (dev->driver_info->quirks & KVASER_USB_QUIRK_IGNORE_CLK_FREQ) {
|
||||
/* Firmware expects bittiming parameters calculated for 16MHz
|
||||
* clock, regardless of the actual clock
|
||||
|
@ -693,6 +762,116 @@ static int kvaser_usb_leaf_get_card_info(struct kvaser_usb *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_leaf_get_single_capability(struct kvaser_usb *dev,
|
||||
u16 cap_cmd_req, u16 *status)
|
||||
{
|
||||
struct kvaser_usb_dev_card_data *card_data = &dev->card_data;
|
||||
struct kvaser_cmd *cmd;
|
||||
u32 value = 0;
|
||||
u32 mask = 0;
|
||||
u16 cap_cmd_res;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->id = CMD_GET_CAPABILITIES_REQ;
|
||||
cmd->u.leaf.cap_req.cap_cmd = cpu_to_le16(cap_cmd_req);
|
||||
cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_cap_req);
|
||||
|
||||
err = kvaser_usb_send_cmd(dev, cmd, cmd->len);
|
||||
if (err)
|
||||
goto end;
|
||||
|
||||
err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_CAPABILITIES_RESP, cmd);
|
||||
if (err)
|
||||
goto end;
|
||||
|
||||
*status = le16_to_cpu(cmd->u.leaf.cap_res.status);
|
||||
|
||||
if (*status != KVASER_USB_LEAF_CAP_STAT_OK)
|
||||
goto end;
|
||||
|
||||
cap_cmd_res = le16_to_cpu(cmd->u.leaf.cap_res.cap_cmd);
|
||||
switch (cap_cmd_res) {
|
||||
case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE:
|
||||
case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT:
|
||||
value = le32_to_cpu(cmd->u.leaf.cap_res.value);
|
||||
mask = le32_to_cpu(cmd->u.leaf.cap_res.mask);
|
||||
break;
|
||||
default:
|
||||
dev_warn(&dev->intf->dev, "Unknown capability command %u\n",
|
||||
cap_cmd_res);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < dev->nchannels; i++) {
|
||||
if (BIT(i) & (value & mask)) {
|
||||
switch (cap_cmd_res) {
|
||||
case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE:
|
||||
card_data->ctrlmode_supported |=
|
||||
CAN_CTRLMODE_LISTENONLY;
|
||||
break;
|
||||
case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT:
|
||||
card_data->capabilities |=
|
||||
KVASER_USB_CAP_BERR_CAP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
kfree(cmd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int kvaser_usb_leaf_get_capabilities_leaf(struct kvaser_usb *dev)
|
||||
{
|
||||
int err;
|
||||
u16 status;
|
||||
|
||||
if (!(dev->card_data.capabilities & KVASER_USB_CAP_EXT_CAP)) {
|
||||
dev_info(&dev->intf->dev,
|
||||
"No extended capability support. Upgrade device firmware.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = kvaser_usb_leaf_get_single_capability(dev,
|
||||
KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE,
|
||||
&status);
|
||||
if (err)
|
||||
return err;
|
||||
if (status)
|
||||
dev_info(&dev->intf->dev,
|
||||
"KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE failed %u\n",
|
||||
status);
|
||||
|
||||
err = kvaser_usb_leaf_get_single_capability(dev,
|
||||
KVASER_USB_LEAF_CAP_CMD_ERR_REPORT,
|
||||
&status);
|
||||
if (err)
|
||||
return err;
|
||||
if (status)
|
||||
dev_info(&dev->intf->dev,
|
||||
"KVASER_USB_LEAF_CAP_CMD_ERR_REPORT failed %u\n",
|
||||
status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_leaf_get_capabilities(struct kvaser_usb *dev)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (dev->driver_info->family == KVASER_LEAF)
|
||||
err = kvaser_usb_leaf_get_capabilities_leaf(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev,
|
||||
const struct kvaser_cmd *cmd)
|
||||
{
|
||||
|
@ -721,7 +900,7 @@ static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev,
|
|||
context = &priv->tx_contexts[tid % dev->max_tx_urbs];
|
||||
|
||||
/* Sometimes the state change doesn't come after a bus-off event */
|
||||
if (priv->can.restart_ms && priv->can.state >= CAN_STATE_BUS_OFF) {
|
||||
if (priv->can.restart_ms && priv->can.state == CAN_STATE_BUS_OFF) {
|
||||
struct sk_buff *skb;
|
||||
struct can_frame *cf;
|
||||
|
||||
|
@ -774,11 +953,22 @@ 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,
|
||||
struct can_frame *cf)
|
||||
{
|
||||
struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv;
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
struct net_device_stats *stats = &priv->netdev->stats;
|
||||
enum can_state cur_state, new_state, tx_state, rx_state;
|
||||
|
@ -792,20 +982,32 @@ kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
|
|||
new_state = CAN_STATE_BUS_OFF;
|
||||
} else if (es->status & M16C_STATE_BUS_PASSIVE) {
|
||||
new_state = CAN_STATE_ERROR_PASSIVE;
|
||||
} else if (es->status & M16C_STATE_BUS_ERROR) {
|
||||
} else if ((es->status & M16C_STATE_BUS_ERROR) &&
|
||||
cur_state >= CAN_STATE_BUS_OFF) {
|
||||
/* Guard against spurious error events after a busoff */
|
||||
if (cur_state < CAN_STATE_BUS_OFF) {
|
||||
if (es->txerr >= 128 || es->rxerr >= 128)
|
||||
new_state = CAN_STATE_ERROR_PASSIVE;
|
||||
else if (es->txerr >= 96 || es->rxerr >= 96)
|
||||
new_state = CAN_STATE_ERROR_WARNING;
|
||||
else if (cur_state > CAN_STATE_ERROR_ACTIVE)
|
||||
new_state = CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
} else if (es->txerr >= 128 || es->rxerr >= 128) {
|
||||
new_state = CAN_STATE_ERROR_PASSIVE;
|
||||
} else if (es->txerr >= 96 || es->rxerr >= 96) {
|
||||
new_state = CAN_STATE_ERROR_WARNING;
|
||||
} else {
|
||||
new_state = CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
|
||||
if (!es->status)
|
||||
new_state = CAN_STATE_ERROR_ACTIVE;
|
||||
/* 0bfd:0124 FW 4.18.778 was observed to send the initial
|
||||
* CMD_CHIP_STATE_EVENT after CMD_START_CHIP with M16C_STATE_BUS_OFF
|
||||
* bit set if the channel was bus-off when it was last stopped (even
|
||||
* across chip resets). This bit will clear shortly afterwards, without
|
||||
* triggering a second unsolicited chip state event.
|
||||
* Ignore this initial bus-off.
|
||||
*/
|
||||
if (leaf->joining_bus) {
|
||||
if (new_state == CAN_STATE_BUS_OFF) {
|
||||
netdev_dbg(priv->netdev, "ignoring bus-off during startup");
|
||||
new_state = cur_state;
|
||||
} else {
|
||||
leaf->joining_bus = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_state != cur_state) {
|
||||
tx_state = (es->txerr >= es->rxerr) ? new_state : 0;
|
||||
|
@ -815,7 +1017,7 @@ kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
|
|||
}
|
||||
|
||||
if (priv->can.restart_ms &&
|
||||
cur_state >= CAN_STATE_BUS_OFF &&
|
||||
cur_state == CAN_STATE_BUS_OFF &&
|
||||
new_state < CAN_STATE_BUS_OFF)
|
||||
priv->can.can_stats.restarts++;
|
||||
|
||||
|
@ -849,6 +1051,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) {
|
||||
|
@ -858,8 +1061,13 @@ 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;
|
||||
|
||||
/* Ignore e.g. state change to bus-off reported just after stopping */
|
||||
if (!netif_running(priv->netdev))
|
||||
return;
|
||||
|
||||
/* Update all of the CAN interface's state and error counters before
|
||||
* trying any memory allocation that can actually fail with -ENOMEM.
|
||||
*
|
||||
|
@ -874,6 +1082,17 @@ 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.
|
||||
* Also request updates if we saw a stale BUS_OFF during startup
|
||||
* (joining_bus).
|
||||
*/
|
||||
if (new_state < CAN_STATE_BUS_OFF &&
|
||||
(es->rxerr || es->txerr || new_state == CAN_STATE_ERROR_PASSIVE ||
|
||||
leaf->joining_bus))
|
||||
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++;
|
||||
|
@ -891,7 +1110,7 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev,
|
|||
}
|
||||
|
||||
if (priv->can.restart_ms &&
|
||||
old_state >= CAN_STATE_BUS_OFF &&
|
||||
old_state == CAN_STATE_BUS_OFF &&
|
||||
new_state < CAN_STATE_BUS_OFF) {
|
||||
cf->can_id |= CAN_ERR_RESTARTED;
|
||||
netif_carrier_on(priv->netdev);
|
||||
|
@ -990,11 +1209,11 @@ static void kvaser_usb_leaf_usbcan_rx_error(const struct kvaser_usb *dev,
|
|||
|
||||
case CMD_CAN_ERROR_EVENT:
|
||||
es.channel = 0;
|
||||
es.status = cmd->u.usbcan.error_event.status_ch0;
|
||||
es.txerr = cmd->u.usbcan.error_event.tx_errors_count_ch0;
|
||||
es.rxerr = cmd->u.usbcan.error_event.rx_errors_count_ch0;
|
||||
es.status = cmd->u.usbcan.can_error_event.status_ch0;
|
||||
es.txerr = cmd->u.usbcan.can_error_event.tx_errors_count_ch0;
|
||||
es.rxerr = cmd->u.usbcan.can_error_event.rx_errors_count_ch0;
|
||||
es.usbcan.other_ch_status =
|
||||
cmd->u.usbcan.error_event.status_ch1;
|
||||
cmd->u.usbcan.can_error_event.status_ch1;
|
||||
kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es);
|
||||
|
||||
/* The USBCAN firmware supports up to 2 channels.
|
||||
|
@ -1002,13 +1221,13 @@ static void kvaser_usb_leaf_usbcan_rx_error(const struct kvaser_usb *dev,
|
|||
*/
|
||||
if (dev->nchannels == MAX_USBCAN_NET_DEVICES) {
|
||||
es.channel = 1;
|
||||
es.status = cmd->u.usbcan.error_event.status_ch1;
|
||||
es.status = cmd->u.usbcan.can_error_event.status_ch1;
|
||||
es.txerr =
|
||||
cmd->u.usbcan.error_event.tx_errors_count_ch1;
|
||||
cmd->u.usbcan.can_error_event.tx_errors_count_ch1;
|
||||
es.rxerr =
|
||||
cmd->u.usbcan.error_event.rx_errors_count_ch1;
|
||||
cmd->u.usbcan.can_error_event.rx_errors_count_ch1;
|
||||
es.usbcan.other_ch_status =
|
||||
cmd->u.usbcan.error_event.status_ch0;
|
||||
cmd->u.usbcan.can_error_event.status_ch0;
|
||||
kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es);
|
||||
}
|
||||
break;
|
||||
|
@ -1025,11 +1244,11 @@ static void kvaser_usb_leaf_leaf_rx_error(const struct kvaser_usb *dev,
|
|||
|
||||
switch (cmd->id) {
|
||||
case CMD_CAN_ERROR_EVENT:
|
||||
es.channel = cmd->u.leaf.error_event.channel;
|
||||
es.status = cmd->u.leaf.error_event.status;
|
||||
es.txerr = cmd->u.leaf.error_event.tx_errors_count;
|
||||
es.rxerr = cmd->u.leaf.error_event.rx_errors_count;
|
||||
es.leaf.error_factor = cmd->u.leaf.error_event.error_factor;
|
||||
es.channel = cmd->u.leaf.can_error_event.channel;
|
||||
es.status = cmd->u.leaf.can_error_event.status;
|
||||
es.txerr = cmd->u.leaf.can_error_event.tx_errors_count;
|
||||
es.rxerr = cmd->u.leaf.can_error_event.rx_errors_count;
|
||||
es.leaf.error_factor = cmd->u.leaf.can_error_event.error_factor;
|
||||
break;
|
||||
case CMD_LEAF_LOG_MESSAGE:
|
||||
es.channel = cmd->u.leaf.log_message.channel;
|
||||
|
@ -1162,6 +1381,74 @@ static void kvaser_usb_leaf_rx_can_msg(const struct kvaser_usb *dev,
|
|||
netif_rx(skb);
|
||||
}
|
||||
|
||||
static void kvaser_usb_leaf_error_event_parameter(const struct kvaser_usb *dev,
|
||||
const struct kvaser_cmd *cmd)
|
||||
{
|
||||
u16 info1 = 0;
|
||||
|
||||
switch (dev->driver_info->family) {
|
||||
case KVASER_LEAF:
|
||||
info1 = le16_to_cpu(cmd->u.leaf.error_event.info1);
|
||||
break;
|
||||
case KVASER_USBCAN:
|
||||
info1 = le16_to_cpu(cmd->u.usbcan.error_event.info1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* info1 will contain the offending cmd_no */
|
||||
switch (info1) {
|
||||
case CMD_SET_CTRL_MODE:
|
||||
dev_warn(&dev->intf->dev,
|
||||
"CMD_SET_CTRL_MODE error in parameter\n");
|
||||
break;
|
||||
|
||||
case CMD_SET_BUS_PARAMS:
|
||||
dev_warn(&dev->intf->dev,
|
||||
"CMD_SET_BUS_PARAMS error in parameter\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(&dev->intf->dev,
|
||||
"Unhandled parameter error event cmd_no (%u)\n",
|
||||
info1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void kvaser_usb_leaf_error_event(const struct kvaser_usb *dev,
|
||||
const struct kvaser_cmd *cmd)
|
||||
{
|
||||
u8 error_code = 0;
|
||||
|
||||
switch (dev->driver_info->family) {
|
||||
case KVASER_LEAF:
|
||||
error_code = cmd->u.leaf.error_event.error_code;
|
||||
break;
|
||||
case KVASER_USBCAN:
|
||||
error_code = cmd->u.usbcan.error_event.error_code;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (error_code) {
|
||||
case KVASER_USB_LEAF_ERROR_EVENT_TX_QUEUE_FULL:
|
||||
/* Received additional CAN message, when firmware TX queue is
|
||||
* already full. Something is wrong with the driver.
|
||||
* This should never happen!
|
||||
*/
|
||||
dev_err(&dev->intf->dev,
|
||||
"Received error event TX_QUEUE_FULL\n");
|
||||
break;
|
||||
case KVASER_USB_LEAF_ERROR_EVENT_PARAM:
|
||||
kvaser_usb_leaf_error_event_parameter(dev, cmd);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(&dev->intf->dev,
|
||||
"Unhandled error event (%d)\n", error_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void kvaser_usb_leaf_start_chip_reply(const struct kvaser_usb *dev,
|
||||
const struct kvaser_cmd *cmd)
|
||||
{
|
||||
|
@ -1202,6 +1489,25 @@ static void kvaser_usb_leaf_stop_chip_reply(const struct kvaser_usb *dev,
|
|||
complete(&priv->stop_comp);
|
||||
}
|
||||
|
||||
static void kvaser_usb_leaf_get_busparams_reply(const struct kvaser_usb *dev,
|
||||
const struct kvaser_cmd *cmd)
|
||||
{
|
||||
struct kvaser_usb_net_priv *priv;
|
||||
u8 channel = cmd->u.busparams.channel;
|
||||
|
||||
if (channel >= dev->nchannels) {
|
||||
dev_err(&dev->intf->dev,
|
||||
"Invalid channel number (%d)\n", channel);
|
||||
return;
|
||||
}
|
||||
|
||||
priv = dev->nets[channel];
|
||||
memcpy(&priv->busparams_nominal, &cmd->u.busparams.busparams,
|
||||
sizeof(priv->busparams_nominal));
|
||||
|
||||
complete(&priv->get_busparams_comp);
|
||||
}
|
||||
|
||||
static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev,
|
||||
const struct kvaser_cmd *cmd)
|
||||
{
|
||||
|
@ -1240,6 +1546,14 @@ static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev,
|
|||
kvaser_usb_leaf_tx_acknowledge(dev, cmd);
|
||||
break;
|
||||
|
||||
case CMD_ERROR_EVENT:
|
||||
kvaser_usb_leaf_error_event(dev, cmd);
|
||||
break;
|
||||
|
||||
case CMD_GET_BUS_PARAMS_REPLY:
|
||||
kvaser_usb_leaf_get_busparams_reply(dev, cmd);
|
||||
break;
|
||||
|
||||
/* Ignored commands */
|
||||
case CMD_USBCAN_CLOCK_OVERFLOW_EVENT:
|
||||
if (dev->driver_info->family != KVASER_USBCAN)
|
||||
|
@ -1318,8 +1632,11 @@ static int kvaser_usb_leaf_set_opt_mode(const struct kvaser_usb_net_priv *priv)
|
|||
|
||||
static int kvaser_usb_leaf_start_chip(struct kvaser_usb_net_priv *priv)
|
||||
{
|
||||
struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv;
|
||||
int err;
|
||||
|
||||
leaf->joining_bus = true;
|
||||
|
||||
init_completion(&priv->start_comp);
|
||||
|
||||
err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_START_CHIP,
|
||||
|
@ -1336,10 +1653,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;
|
||||
|
||||
init_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)
|
||||
|
@ -1386,10 +1706,35 @@ static int kvaser_usb_leaf_init_card(struct kvaser_usb *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev)
|
||||
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(const struct net_device *netdev,
|
||||
const struct kvaser_usb_busparams *busparams)
|
||||
{
|
||||
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
|
||||
struct can_bittiming *bt = &priv->can.bittiming;
|
||||
struct kvaser_usb *dev = priv->dev;
|
||||
struct kvaser_cmd *cmd;
|
||||
int rc;
|
||||
|
@ -1402,15 +1747,8 @@ static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev)
|
|||
cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_busparams);
|
||||
cmd->u.busparams.channel = priv->channel;
|
||||
cmd->u.busparams.tid = 0xff;
|
||||
cmd->u.busparams.bitrate = cpu_to_le32(bt->bitrate);
|
||||
cmd->u.busparams.sjw = bt->sjw;
|
||||
cmd->u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1;
|
||||
cmd->u.busparams.tseg2 = bt->phase_seg2;
|
||||
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
|
||||
cmd->u.busparams.no_samp = 3;
|
||||
else
|
||||
cmd->u.busparams.no_samp = 1;
|
||||
memcpy(&cmd->u.busparams.busparams, busparams,
|
||||
sizeof(cmd->u.busparams.busparams));
|
||||
|
||||
rc = kvaser_usb_send_cmd(dev, cmd, cmd->len);
|
||||
|
||||
|
@ -1418,16 +1756,40 @@ static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int kvaser_usb_leaf_get_busparams(struct kvaser_usb_net_priv *priv)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (priv->dev->driver_info->family == KVASER_USBCAN)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
reinit_completion(&priv->get_busparams_comp);
|
||||
|
||||
err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_GET_BUS_PARAMS,
|
||||
priv->channel);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!wait_for_completion_timeout(&priv->get_busparams_comp,
|
||||
msecs_to_jiffies(KVASER_USB_TIMEOUT)))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvaser_usb_leaf_set_mode(struct net_device *netdev,
|
||||
enum can_mode mode)
|
||||
{
|
||||
struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
|
||||
struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv;
|
||||
int err;
|
||||
|
||||
switch (mode) {
|
||||
case CAN_MODE_START:
|
||||
kvaser_usb_unlink_tx_urbs(priv);
|
||||
|
||||
leaf->joining_bus = true;
|
||||
|
||||
err = kvaser_usb_leaf_simple_cmd_async(priv, CMD_START_CHIP);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -1479,14 +1841,18 @@ static int kvaser_usb_leaf_setup_endpoints(struct kvaser_usb *dev)
|
|||
const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = {
|
||||
.dev_set_mode = kvaser_usb_leaf_set_mode,
|
||||
.dev_set_bittiming = kvaser_usb_leaf_set_bittiming,
|
||||
.dev_get_busparams = kvaser_usb_leaf_get_busparams,
|
||||
.dev_set_data_bittiming = NULL,
|
||||
.dev_get_data_busparams = NULL,
|
||||
.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,
|
||||
.dev_get_capabilities = NULL,
|
||||
.dev_get_capabilities = kvaser_usb_leaf_get_capabilities,
|
||||
.dev_set_opt_mode = kvaser_usb_leaf_set_opt_mode,
|
||||
.dev_start_chip = kvaser_usb_leaf_start_chip,
|
||||
.dev_stop_chip = kvaser_usb_leaf_stop_chip,
|
||||
|
|
Loading…
Reference in New Issue