|
@@ -6,10 +6,12 @@
|
|
* Parts of this driver are based on the following:
|
|
* Parts of this driver are based on the following:
|
|
* - Kvaser linux leaf driver (version 4.78)
|
|
* - Kvaser linux leaf driver (version 4.78)
|
|
* - CAN driver for esd CAN-USB/2
|
|
* - CAN driver for esd CAN-USB/2
|
|
|
|
+ * - Kvaser linux usbcanII driver (version 5.3)
|
|
*
|
|
*
|
|
* Copyright (C) 2002-2006 KVASER AB, Sweden. All rights reserved.
|
|
* Copyright (C) 2002-2006 KVASER AB, Sweden. All rights reserved.
|
|
* Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh
|
|
* Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh
|
|
* Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be>
|
|
* Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be>
|
|
|
|
+ * Copyright (C) 2015 Valeo A.S.
|
|
*/
|
|
*/
|
|
|
|
|
|
#include <linux/completion.h>
|
|
#include <linux/completion.h>
|
|
@@ -30,8 +32,9 @@
|
|
#define RX_BUFFER_SIZE 3072
|
|
#define RX_BUFFER_SIZE 3072
|
|
#define CAN_USB_CLOCK 8000000
|
|
#define CAN_USB_CLOCK 8000000
|
|
#define MAX_NET_DEVICES 3
|
|
#define MAX_NET_DEVICES 3
|
|
|
|
+#define MAX_USBCAN_NET_DEVICES 2
|
|
|
|
|
|
-/* Kvaser USB devices */
|
|
|
|
|
|
+/* Kvaser Leaf USB devices */
|
|
#define KVASER_VENDOR_ID 0x0bfd
|
|
#define KVASER_VENDOR_ID 0x0bfd
|
|
#define USB_LEAF_DEVEL_PRODUCT_ID 10
|
|
#define USB_LEAF_DEVEL_PRODUCT_ID 10
|
|
#define USB_LEAF_LITE_PRODUCT_ID 11
|
|
#define USB_LEAF_LITE_PRODUCT_ID 11
|
|
@@ -56,6 +59,24 @@
|
|
#define USB_LEAF_LITE_V2_PRODUCT_ID 288
|
|
#define USB_LEAF_LITE_V2_PRODUCT_ID 288
|
|
#define USB_MINI_PCIE_HS_PRODUCT_ID 289
|
|
#define USB_MINI_PCIE_HS_PRODUCT_ID 289
|
|
|
|
|
|
|
|
+static inline bool kvaser_is_leaf(const struct usb_device_id *id)
|
|
|
|
+{
|
|
|
|
+ return id->idProduct >= USB_LEAF_DEVEL_PRODUCT_ID &&
|
|
|
|
+ id->idProduct <= USB_MINI_PCIE_HS_PRODUCT_ID;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Kvaser USBCan-II devices */
|
|
|
|
+#define USB_USBCAN_REVB_PRODUCT_ID 2
|
|
|
|
+#define USB_VCI2_PRODUCT_ID 3
|
|
|
|
+#define USB_USBCAN2_PRODUCT_ID 4
|
|
|
|
+#define USB_MEMORATOR_PRODUCT_ID 5
|
|
|
|
+
|
|
|
|
+static inline bool kvaser_is_usbcan(const struct usb_device_id *id)
|
|
|
|
+{
|
|
|
|
+ return id->idProduct >= USB_USBCAN_REVB_PRODUCT_ID &&
|
|
|
|
+ id->idProduct <= USB_MEMORATOR_PRODUCT_ID;
|
|
|
|
+}
|
|
|
|
+
|
|
/* USB devices features */
|
|
/* USB devices features */
|
|
#define KVASER_HAS_SILENT_MODE BIT(0)
|
|
#define KVASER_HAS_SILENT_MODE BIT(0)
|
|
#define KVASER_HAS_TXRX_ERRORS BIT(1)
|
|
#define KVASER_HAS_TXRX_ERRORS BIT(1)
|
|
@@ -73,7 +94,7 @@
|
|
#define MSG_FLAG_TX_ACK BIT(6)
|
|
#define MSG_FLAG_TX_ACK BIT(6)
|
|
#define MSG_FLAG_TX_REQUEST BIT(7)
|
|
#define MSG_FLAG_TX_REQUEST BIT(7)
|
|
|
|
|
|
-/* Can states */
|
|
|
|
|
|
+/* Can states (M16C CxSTRH register) */
|
|
#define M16C_STATE_BUS_RESET BIT(0)
|
|
#define M16C_STATE_BUS_RESET BIT(0)
|
|
#define M16C_STATE_BUS_ERROR BIT(4)
|
|
#define M16C_STATE_BUS_ERROR BIT(4)
|
|
#define M16C_STATE_BUS_PASSIVE BIT(5)
|
|
#define M16C_STATE_BUS_PASSIVE BIT(5)
|
|
@@ -98,7 +119,11 @@
|
|
#define CMD_START_CHIP_REPLY 27
|
|
#define CMD_START_CHIP_REPLY 27
|
|
#define CMD_STOP_CHIP 28
|
|
#define CMD_STOP_CHIP 28
|
|
#define CMD_STOP_CHIP_REPLY 29
|
|
#define CMD_STOP_CHIP_REPLY 29
|
|
-#define CMD_GET_CARD_INFO2 32
|
|
|
|
|
|
+
|
|
|
|
+#define CMD_LEAF_GET_CARD_INFO2 32
|
|
|
|
+#define CMD_USBCAN_RESET_CLOCK 32
|
|
|
|
+#define CMD_USBCAN_CLOCK_OVERFLOW_EVENT 33
|
|
|
|
+
|
|
#define CMD_GET_CARD_INFO 34
|
|
#define CMD_GET_CARD_INFO 34
|
|
#define CMD_GET_CARD_INFO_REPLY 35
|
|
#define CMD_GET_CARD_INFO_REPLY 35
|
|
#define CMD_GET_SOFTWARE_INFO 38
|
|
#define CMD_GET_SOFTWARE_INFO 38
|
|
@@ -108,8 +133,9 @@
|
|
#define CMD_RESET_ERROR_COUNTER 49
|
|
#define CMD_RESET_ERROR_COUNTER 49
|
|
#define CMD_TX_ACKNOWLEDGE 50
|
|
#define CMD_TX_ACKNOWLEDGE 50
|
|
#define CMD_CAN_ERROR_EVENT 51
|
|
#define CMD_CAN_ERROR_EVENT 51
|
|
-#define CMD_USB_THROTTLE 77
|
|
|
|
-#define CMD_LOG_MESSAGE 106
|
|
|
|
|
|
+
|
|
|
|
+#define CMD_LEAF_USB_THROTTLE 77
|
|
|
|
+#define CMD_LEAF_LOG_MESSAGE 106
|
|
|
|
|
|
/* error factors */
|
|
/* error factors */
|
|
#define M16C_EF_ACKE BIT(0)
|
|
#define M16C_EF_ACKE BIT(0)
|
|
@@ -121,6 +147,14 @@
|
|
#define M16C_EF_RCVE BIT(6)
|
|
#define M16C_EF_RCVE BIT(6)
|
|
#define M16C_EF_TRE BIT(7)
|
|
#define M16C_EF_TRE BIT(7)
|
|
|
|
|
|
|
|
+/* Only Leaf-based devices can report M16C error factors,
|
|
|
|
+ * thus define our own error status flags for USBCANII
|
|
|
|
+ */
|
|
|
|
+#define USBCAN_ERROR_STATE_NONE 0
|
|
|
|
+#define USBCAN_ERROR_STATE_TX_ERROR BIT(0)
|
|
|
|
+#define USBCAN_ERROR_STATE_RX_ERROR BIT(1)
|
|
|
|
+#define USBCAN_ERROR_STATE_BUSERROR BIT(2)
|
|
|
|
+
|
|
/* bittiming parameters */
|
|
/* bittiming parameters */
|
|
#define KVASER_USB_TSEG1_MIN 1
|
|
#define KVASER_USB_TSEG1_MIN 1
|
|
#define KVASER_USB_TSEG1_MAX 16
|
|
#define KVASER_USB_TSEG1_MAX 16
|
|
@@ -137,9 +171,18 @@
|
|
#define KVASER_CTRL_MODE_SELFRECEPTION 3
|
|
#define KVASER_CTRL_MODE_SELFRECEPTION 3
|
|
#define KVASER_CTRL_MODE_OFF 4
|
|
#define KVASER_CTRL_MODE_OFF 4
|
|
|
|
|
|
-/* log message */
|
|
|
|
|
|
+/* Extended CAN identifier flag */
|
|
#define KVASER_EXTENDED_FRAME BIT(31)
|
|
#define KVASER_EXTENDED_FRAME BIT(31)
|
|
|
|
|
|
|
|
+/* Kvaser USB CAN dongles are divided into two major families:
|
|
|
|
+ * - Leaf: Based on Renesas M32C, running firmware labeled as 'filo'
|
|
|
|
+ * - UsbcanII: Based on Renesas M16C, running firmware labeled as 'helios'
|
|
|
|
+ */
|
|
|
|
+enum kvaser_usb_family {
|
|
|
|
+ KVASER_LEAF,
|
|
|
|
+ KVASER_USBCAN,
|
|
|
|
+};
|
|
|
|
+
|
|
struct kvaser_msg_simple {
|
|
struct kvaser_msg_simple {
|
|
u8 tid;
|
|
u8 tid;
|
|
u8 channel;
|
|
u8 channel;
|
|
@@ -148,30 +191,55 @@ struct kvaser_msg_simple {
|
|
struct kvaser_msg_cardinfo {
|
|
struct kvaser_msg_cardinfo {
|
|
u8 tid;
|
|
u8 tid;
|
|
u8 nchannels;
|
|
u8 nchannels;
|
|
- __le32 serial_number;
|
|
|
|
- __le32 padding;
|
|
|
|
|
|
+ union {
|
|
|
|
+ struct {
|
|
|
|
+ __le32 serial_number;
|
|
|
|
+ __le32 padding;
|
|
|
|
+ } __packed leaf0;
|
|
|
|
+ struct {
|
|
|
|
+ __le32 serial_number_low;
|
|
|
|
+ __le32 serial_number_high;
|
|
|
|
+ } __packed usbcan0;
|
|
|
|
+ } __packed;
|
|
__le32 clock_resolution;
|
|
__le32 clock_resolution;
|
|
__le32 mfgdate;
|
|
__le32 mfgdate;
|
|
u8 ean[8];
|
|
u8 ean[8];
|
|
u8 hw_revision;
|
|
u8 hw_revision;
|
|
- u8 usb_hs_mode;
|
|
|
|
- __le16 padding2;
|
|
|
|
|
|
+ union {
|
|
|
|
+ struct {
|
|
|
|
+ u8 usb_hs_mode;
|
|
|
|
+ } __packed leaf1;
|
|
|
|
+ struct {
|
|
|
|
+ u8 padding;
|
|
|
|
+ } __packed usbcan1;
|
|
|
|
+ } __packed;
|
|
|
|
+ __le16 padding;
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
struct kvaser_msg_cardinfo2 {
|
|
struct kvaser_msg_cardinfo2 {
|
|
u8 tid;
|
|
u8 tid;
|
|
- u8 channel;
|
|
|
|
|
|
+ u8 reserved;
|
|
u8 pcb_id[24];
|
|
u8 pcb_id[24];
|
|
__le32 oem_unlock_code;
|
|
__le32 oem_unlock_code;
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
-struct kvaser_msg_softinfo {
|
|
|
|
|
|
+struct leaf_msg_softinfo {
|
|
u8 tid;
|
|
u8 tid;
|
|
- u8 channel;
|
|
|
|
|
|
+ u8 padding0;
|
|
__le32 sw_options;
|
|
__le32 sw_options;
|
|
__le32 fw_version;
|
|
__le32 fw_version;
|
|
__le16 max_outstanding_tx;
|
|
__le16 max_outstanding_tx;
|
|
- __le16 padding[9];
|
|
|
|
|
|
+ __le16 padding1[9];
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+struct usbcan_msg_softinfo {
|
|
|
|
+ u8 tid;
|
|
|
|
+ u8 fw_name[5];
|
|
|
|
+ __le16 max_outstanding_tx;
|
|
|
|
+ u8 padding[6];
|
|
|
|
+ __le32 fw_version;
|
|
|
|
+ __le16 checksum;
|
|
|
|
+ __le16 sw_options;
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
struct kvaser_msg_busparams {
|
|
struct kvaser_msg_busparams {
|
|
@@ -188,36 +256,86 @@ struct kvaser_msg_tx_can {
|
|
u8 channel;
|
|
u8 channel;
|
|
u8 tid;
|
|
u8 tid;
|
|
u8 msg[14];
|
|
u8 msg[14];
|
|
- u8 padding;
|
|
|
|
- u8 flags;
|
|
|
|
|
|
+ union {
|
|
|
|
+ struct {
|
|
|
|
+ u8 padding;
|
|
|
|
+ u8 flags;
|
|
|
|
+ } __packed leaf;
|
|
|
|
+ struct {
|
|
|
|
+ u8 flags;
|
|
|
|
+ u8 padding;
|
|
|
|
+ } __packed usbcan;
|
|
|
|
+ } __packed;
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+struct kvaser_msg_rx_can_header {
|
|
|
|
+ u8 channel;
|
|
|
|
+ u8 flag;
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
-struct kvaser_msg_rx_can {
|
|
|
|
|
|
+struct leaf_msg_rx_can {
|
|
u8 channel;
|
|
u8 channel;
|
|
u8 flag;
|
|
u8 flag;
|
|
|
|
+
|
|
__le16 time[3];
|
|
__le16 time[3];
|
|
u8 msg[14];
|
|
u8 msg[14];
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
-struct kvaser_msg_chip_state_event {
|
|
|
|
|
|
+struct usbcan_msg_rx_can {
|
|
|
|
+ u8 channel;
|
|
|
|
+ u8 flag;
|
|
|
|
+
|
|
|
|
+ u8 msg[14];
|
|
|
|
+ __le16 time;
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+struct leaf_msg_chip_state_event {
|
|
u8 tid;
|
|
u8 tid;
|
|
u8 channel;
|
|
u8 channel;
|
|
|
|
+
|
|
__le16 time[3];
|
|
__le16 time[3];
|
|
u8 tx_errors_count;
|
|
u8 tx_errors_count;
|
|
u8 rx_errors_count;
|
|
u8 rx_errors_count;
|
|
|
|
+
|
|
|
|
+ u8 status;
|
|
|
|
+ u8 padding[3];
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+struct usbcan_msg_chip_state_event {
|
|
|
|
+ u8 tid;
|
|
|
|
+ u8 channel;
|
|
|
|
+
|
|
|
|
+ u8 tx_errors_count;
|
|
|
|
+ u8 rx_errors_count;
|
|
|
|
+ __le16 time;
|
|
|
|
+
|
|
u8 status;
|
|
u8 status;
|
|
u8 padding[3];
|
|
u8 padding[3];
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
-struct kvaser_msg_tx_acknowledge {
|
|
|
|
|
|
+struct kvaser_msg_tx_acknowledge_header {
|
|
|
|
+ u8 channel;
|
|
|
|
+ u8 tid;
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+struct leaf_msg_tx_acknowledge {
|
|
u8 channel;
|
|
u8 channel;
|
|
u8 tid;
|
|
u8 tid;
|
|
|
|
+
|
|
__le16 time[3];
|
|
__le16 time[3];
|
|
u8 flags;
|
|
u8 flags;
|
|
u8 time_offset;
|
|
u8 time_offset;
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
-struct kvaser_msg_error_event {
|
|
|
|
|
|
+struct usbcan_msg_tx_acknowledge {
|
|
|
|
+ u8 channel;
|
|
|
|
+ u8 tid;
|
|
|
|
+
|
|
|
|
+ __le16 time;
|
|
|
|
+ __le16 padding;
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+struct leaf_msg_error_event {
|
|
u8 tid;
|
|
u8 tid;
|
|
u8 flags;
|
|
u8 flags;
|
|
__le16 time[3];
|
|
__le16 time[3];
|
|
@@ -229,6 +347,18 @@ struct kvaser_msg_error_event {
|
|
u8 error_factor;
|
|
u8 error_factor;
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
|
|
+struct usbcan_msg_error_event {
|
|
|
|
+ u8 tid;
|
|
|
|
+ u8 padding;
|
|
|
|
+ u8 tx_errors_count_ch0;
|
|
|
|
+ u8 rx_errors_count_ch0;
|
|
|
|
+ u8 tx_errors_count_ch1;
|
|
|
|
+ u8 rx_errors_count_ch1;
|
|
|
|
+ u8 status_ch0;
|
|
|
|
+ u8 status_ch1;
|
|
|
|
+ __le16 time;
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
struct kvaser_msg_ctrl_mode {
|
|
struct kvaser_msg_ctrl_mode {
|
|
u8 tid;
|
|
u8 tid;
|
|
u8 channel;
|
|
u8 channel;
|
|
@@ -243,7 +373,7 @@ struct kvaser_msg_flush_queue {
|
|
u8 padding[3];
|
|
u8 padding[3];
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
-struct kvaser_msg_log_message {
|
|
|
|
|
|
+struct leaf_msg_log_message {
|
|
u8 channel;
|
|
u8 channel;
|
|
u8 flags;
|
|
u8 flags;
|
|
__le16 time[3];
|
|
__le16 time[3];
|
|
@@ -260,19 +390,57 @@ struct kvaser_msg {
|
|
struct kvaser_msg_simple simple;
|
|
struct kvaser_msg_simple simple;
|
|
struct kvaser_msg_cardinfo cardinfo;
|
|
struct kvaser_msg_cardinfo cardinfo;
|
|
struct kvaser_msg_cardinfo2 cardinfo2;
|
|
struct kvaser_msg_cardinfo2 cardinfo2;
|
|
- struct kvaser_msg_softinfo softinfo;
|
|
|
|
struct kvaser_msg_busparams busparams;
|
|
struct kvaser_msg_busparams busparams;
|
|
|
|
+
|
|
|
|
+ struct kvaser_msg_rx_can_header rx_can_header;
|
|
|
|
+ struct kvaser_msg_tx_acknowledge_header tx_acknowledge_header;
|
|
|
|
+
|
|
|
|
+ union {
|
|
|
|
+ struct leaf_msg_softinfo softinfo;
|
|
|
|
+ struct leaf_msg_rx_can rx_can;
|
|
|
|
+ struct leaf_msg_chip_state_event chip_state_event;
|
|
|
|
+ struct leaf_msg_tx_acknowledge tx_acknowledge;
|
|
|
|
+ struct leaf_msg_error_event error_event;
|
|
|
|
+ struct leaf_msg_log_message log_message;
|
|
|
|
+ } __packed leaf;
|
|
|
|
+
|
|
|
|
+ union {
|
|
|
|
+ struct usbcan_msg_softinfo softinfo;
|
|
|
|
+ struct usbcan_msg_rx_can rx_can;
|
|
|
|
+ struct usbcan_msg_chip_state_event chip_state_event;
|
|
|
|
+ struct usbcan_msg_tx_acknowledge tx_acknowledge;
|
|
|
|
+ struct usbcan_msg_error_event error_event;
|
|
|
|
+ } __packed usbcan;
|
|
|
|
+
|
|
struct kvaser_msg_tx_can tx_can;
|
|
struct kvaser_msg_tx_can tx_can;
|
|
- struct kvaser_msg_rx_can rx_can;
|
|
|
|
- struct kvaser_msg_chip_state_event chip_state_event;
|
|
|
|
- struct kvaser_msg_tx_acknowledge tx_acknowledge;
|
|
|
|
- struct kvaser_msg_error_event error_event;
|
|
|
|
struct kvaser_msg_ctrl_mode ctrl_mode;
|
|
struct kvaser_msg_ctrl_mode ctrl_mode;
|
|
struct kvaser_msg_flush_queue flush_queue;
|
|
struct kvaser_msg_flush_queue flush_queue;
|
|
- struct kvaser_msg_log_message log_message;
|
|
|
|
} u;
|
|
} u;
|
|
} __packed;
|
|
} __packed;
|
|
|
|
|
|
|
|
+/* Summary of a kvaser error event, for a unified Leaf/Usbcan error
|
|
|
|
+ * handling. Some discrepancies between the two families exist:
|
|
|
|
+ *
|
|
|
|
+ * - USBCAN firmware does not report M16C "error factors"
|
|
|
|
+ * - USBCAN controllers has difficulties reporting if the raised error
|
|
|
|
+ * event is for ch0 or ch1. They leave such arbitration to the OS
|
|
|
|
+ * driver by letting it compare error counters with previous values
|
|
|
|
+ * and decide the error event's channel. Thus for USBCAN, the channel
|
|
|
|
+ * field is only advisory.
|
|
|
|
+ */
|
|
|
|
+struct kvaser_usb_error_summary {
|
|
|
|
+ u8 channel, status, txerr, rxerr;
|
|
|
|
+ union {
|
|
|
|
+ struct {
|
|
|
|
+ u8 error_factor;
|
|
|
|
+ } leaf;
|
|
|
|
+ struct {
|
|
|
|
+ u8 other_ch_status;
|
|
|
|
+ u8 error_state;
|
|
|
|
+ } usbcan;
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
struct kvaser_usb_tx_urb_context {
|
|
struct kvaser_usb_tx_urb_context {
|
|
struct kvaser_usb_net_priv *priv;
|
|
struct kvaser_usb_net_priv *priv;
|
|
u32 echo_index;
|
|
u32 echo_index;
|
|
@@ -288,6 +456,7 @@ struct kvaser_usb {
|
|
|
|
|
|
u32 fw_version;
|
|
u32 fw_version;
|
|
unsigned int nchannels;
|
|
unsigned int nchannels;
|
|
|
|
+ enum kvaser_usb_family family;
|
|
|
|
|
|
bool rxinitdone;
|
|
bool rxinitdone;
|
|
void *rxbuf[MAX_RX_URBS];
|
|
void *rxbuf[MAX_RX_URBS];
|
|
@@ -311,6 +480,7 @@ struct kvaser_usb_net_priv {
|
|
};
|
|
};
|
|
|
|
|
|
static const struct usb_device_id kvaser_usb_table[] = {
|
|
static const struct usb_device_id kvaser_usb_table[] = {
|
|
|
|
+ /* Leaf family IDs */
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
|
|
@@ -360,6 +530,17 @@ static const struct usb_device_id kvaser_usb_table[] = {
|
|
.driver_info = KVASER_HAS_TXRX_ERRORS },
|
|
.driver_info = KVASER_HAS_TXRX_ERRORS },
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) },
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) },
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) },
|
|
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) },
|
|
|
|
+
|
|
|
|
+ /* USBCANII family IDs */
|
|
|
|
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN2_PRODUCT_ID),
|
|
|
|
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
|
|
|
|
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_REVB_PRODUCT_ID),
|
|
|
|
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
|
|
|
|
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMORATOR_PRODUCT_ID),
|
|
|
|
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
|
|
|
|
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_VCI2_PRODUCT_ID),
|
|
|
|
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
|
|
|
|
+
|
|
{ }
|
|
{ }
|
|
};
|
|
};
|
|
MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
|
|
MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
|
|
@@ -463,7 +644,14 @@ static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
|
|
if (err)
|
|
if (err)
|
|
return err;
|
|
return err;
|
|
|
|
|
|
- dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
|
|
|
|
|
|
+ switch (dev->family) {
|
|
|
|
+ case KVASER_LEAF:
|
|
|
|
+ dev->fw_version = le32_to_cpu(msg.u.leaf.softinfo.fw_version);
|
|
|
|
+ break;
|
|
|
|
+ case KVASER_USBCAN:
|
|
|
|
+ dev->fw_version = le32_to_cpu(msg.u.usbcan.softinfo.fw_version);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -482,7 +670,9 @@ static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
|
|
return err;
|
|
return err;
|
|
|
|
|
|
dev->nchannels = msg.u.cardinfo.nchannels;
|
|
dev->nchannels = msg.u.cardinfo.nchannels;
|
|
- if (dev->nchannels > MAX_NET_DEVICES)
|
|
|
|
|
|
+ if ((dev->nchannels > MAX_NET_DEVICES) ||
|
|
|
|
+ (dev->family == KVASER_USBCAN &&
|
|
|
|
+ dev->nchannels > MAX_USBCAN_NET_DEVICES))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -496,8 +686,10 @@ static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
|
|
struct kvaser_usb_net_priv *priv;
|
|
struct kvaser_usb_net_priv *priv;
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
struct can_frame *cf;
|
|
struct can_frame *cf;
|
|
- u8 channel = msg->u.tx_acknowledge.channel;
|
|
|
|
- u8 tid = msg->u.tx_acknowledge.tid;
|
|
|
|
|
|
+ u8 channel, tid;
|
|
|
|
+
|
|
|
|
+ channel = msg->u.tx_acknowledge_header.channel;
|
|
|
|
+ tid = msg->u.tx_acknowledge_header.tid;
|
|
|
|
|
|
if (channel >= dev->nchannels) {
|
|
if (channel >= dev->nchannels) {
|
|
dev_err(dev->udev->dev.parent,
|
|
dev_err(dev->udev->dev.parent,
|
|
@@ -615,158 +807,279 @@ static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
|
|
priv->tx_contexts[i].echo_index = MAX_TX_URBS;
|
|
priv->tx_contexts[i].echo_index = MAX_TX_URBS;
|
|
}
|
|
}
|
|
|
|
|
|
-static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
|
|
|
|
- const struct kvaser_msg *msg)
|
|
|
|
|
|
+static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
|
|
|
|
+ const struct kvaser_usb_error_summary *es,
|
|
|
|
+ struct can_frame *cf)
|
|
{
|
|
{
|
|
- struct can_frame *cf;
|
|
|
|
- struct sk_buff *skb;
|
|
|
|
- struct net_device_stats *stats;
|
|
|
|
- struct kvaser_usb_net_priv *priv;
|
|
|
|
- unsigned int new_state;
|
|
|
|
- u8 channel, status, txerr, rxerr, error_factor;
|
|
|
|
|
|
+ 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;
|
|
|
|
|
|
- switch (msg->id) {
|
|
|
|
- case CMD_CAN_ERROR_EVENT:
|
|
|
|
- channel = msg->u.error_event.channel;
|
|
|
|
- status = msg->u.error_event.status;
|
|
|
|
- txerr = msg->u.error_event.tx_errors_count;
|
|
|
|
- rxerr = msg->u.error_event.rx_errors_count;
|
|
|
|
- error_factor = msg->u.error_event.error_factor;
|
|
|
|
- break;
|
|
|
|
- case CMD_LOG_MESSAGE:
|
|
|
|
- channel = msg->u.log_message.channel;
|
|
|
|
- status = msg->u.log_message.data[0];
|
|
|
|
- txerr = msg->u.log_message.data[2];
|
|
|
|
- rxerr = msg->u.log_message.data[3];
|
|
|
|
- error_factor = msg->u.log_message.data[1];
|
|
|
|
|
|
+ netdev_dbg(priv->netdev, "Error status: 0x%02x\n", es->status);
|
|
|
|
+
|
|
|
|
+ new_state = cur_state = priv->can.state;
|
|
|
|
+
|
|
|
|
+ if (es->status & (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET))
|
|
|
|
+ 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) {
|
|
|
|
+ if ((es->txerr >= 256) || (es->rxerr >= 256))
|
|
|
|
+ new_state = CAN_STATE_BUS_OFF;
|
|
|
|
+ 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 if (cur_state > CAN_STATE_ERROR_ACTIVE)
|
|
|
|
+ new_state = CAN_STATE_ERROR_ACTIVE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!es->status)
|
|
|
|
+ new_state = CAN_STATE_ERROR_ACTIVE;
|
|
|
|
+
|
|
|
|
+ if (new_state != cur_state) {
|
|
|
|
+ tx_state = (es->txerr >= es->rxerr) ? new_state : 0;
|
|
|
|
+ rx_state = (es->txerr <= es->rxerr) ? new_state : 0;
|
|
|
|
+
|
|
|
|
+ can_change_state(priv->netdev, cf, tx_state, rx_state);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (priv->can.restart_ms &&
|
|
|
|
+ (cur_state >= CAN_STATE_BUS_OFF) &&
|
|
|
|
+ (new_state < CAN_STATE_BUS_OFF)) {
|
|
|
|
+ priv->can.can_stats.restarts++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (dev->family) {
|
|
|
|
+ case KVASER_LEAF:
|
|
|
|
+ if (es->leaf.error_factor) {
|
|
|
|
+ priv->can.can_stats.bus_error++;
|
|
|
|
+ stats->rx_errors++;
|
|
|
|
+ }
|
|
break;
|
|
break;
|
|
- case CMD_CHIP_STATE_EVENT:
|
|
|
|
- channel = msg->u.chip_state_event.channel;
|
|
|
|
- status = msg->u.chip_state_event.status;
|
|
|
|
- txerr = msg->u.chip_state_event.tx_errors_count;
|
|
|
|
- rxerr = msg->u.chip_state_event.rx_errors_count;
|
|
|
|
- error_factor = 0;
|
|
|
|
|
|
+ case KVASER_USBCAN:
|
|
|
|
+ if (es->usbcan.error_state & USBCAN_ERROR_STATE_TX_ERROR)
|
|
|
|
+ stats->tx_errors++;
|
|
|
|
+ if (es->usbcan.error_state & USBCAN_ERROR_STATE_RX_ERROR)
|
|
|
|
+ stats->rx_errors++;
|
|
|
|
+ if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) {
|
|
|
|
+ priv->can.can_stats.bus_error++;
|
|
|
|
+ }
|
|
break;
|
|
break;
|
|
- default:
|
|
|
|
- dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
|
|
|
|
- msg->id);
|
|
|
|
- return;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- if (channel >= dev->nchannels) {
|
|
|
|
|
|
+ priv->bec.txerr = es->txerr;
|
|
|
|
+ priv->bec.rxerr = es->rxerr;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
|
|
|
|
+ const struct kvaser_usb_error_summary *es)
|
|
|
|
+{
|
|
|
|
+ struct can_frame *cf, tmp_cf = { .can_id = CAN_ERR_FLAG, .can_dlc = CAN_ERR_DLC };
|
|
|
|
+ struct sk_buff *skb;
|
|
|
|
+ struct net_device_stats *stats;
|
|
|
|
+ struct kvaser_usb_net_priv *priv;
|
|
|
|
+ enum can_state old_state, new_state;
|
|
|
|
+
|
|
|
|
+ if (es->channel >= dev->nchannels) {
|
|
dev_err(dev->udev->dev.parent,
|
|
dev_err(dev->udev->dev.parent,
|
|
- "Invalid channel number (%d)\n", channel);
|
|
|
|
|
|
+ "Invalid channel number (%d)\n", es->channel);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- priv = dev->nets[channel];
|
|
|
|
|
|
+ priv = dev->nets[es->channel];
|
|
stats = &priv->netdev->stats;
|
|
stats = &priv->netdev->stats;
|
|
|
|
|
|
|
|
+ /* Update all of the can interface's state and error counters before
|
|
|
|
+ * trying any memory allocation that can actually fail with -ENOMEM.
|
|
|
|
+ *
|
|
|
|
+ * We send a temporary stack-allocated error can frame to
|
|
|
|
+ * can_change_state() for the very same reason.
|
|
|
|
+ *
|
|
|
|
+ * TODO: Split can_change_state() responsibility between updating the
|
|
|
|
+ * can interface's state and counters, and the setting up of can error
|
|
|
|
+ * frame ID and data to userspace. Remove stack allocation afterwards.
|
|
|
|
+ */
|
|
|
|
+ old_state = priv->can.state;
|
|
|
|
+ kvaser_usb_rx_error_update_can_state(priv, es, &tmp_cf);
|
|
|
|
+ new_state = priv->can.state;
|
|
|
|
+
|
|
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++;
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+ memcpy(cf, &tmp_cf, sizeof(*cf));
|
|
|
|
+
|
|
|
|
+ if (new_state != old_state) {
|
|
|
|
+ if (es->status &
|
|
|
|
+ (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) {
|
|
|
|
+ if (!priv->can.restart_ms)
|
|
|
|
+ kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
|
|
|
|
+ netif_carrier_off(priv->netdev);
|
|
|
|
+ }
|
|
|
|
|
|
- new_state = priv->can.state;
|
|
|
|
-
|
|
|
|
- netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
|
|
|
|
-
|
|
|
|
- if (status & (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) {
|
|
|
|
- cf->can_id |= CAN_ERR_BUSOFF;
|
|
|
|
|
|
+ if (priv->can.restart_ms &&
|
|
|
|
+ (old_state >= CAN_STATE_BUS_OFF) &&
|
|
|
|
+ (new_state < CAN_STATE_BUS_OFF)) {
|
|
|
|
+ cf->can_id |= CAN_ERR_RESTARTED;
|
|
|
|
+ netif_carrier_on(priv->netdev);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- priv->can.can_stats.bus_off++;
|
|
|
|
- if (!priv->can.restart_ms)
|
|
|
|
- kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
|
|
|
|
|
|
+ switch (dev->family) {
|
|
|
|
+ case KVASER_LEAF:
|
|
|
|
+ if (es->leaf.error_factor) {
|
|
|
|
+ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
|
|
|
|
+
|
|
|
|
+ if (es->leaf.error_factor & M16C_EF_ACKE)
|
|
|
|
+ cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
|
|
|
|
+ if (es->leaf.error_factor & M16C_EF_CRCE)
|
|
|
|
+ cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
|
|
|
|
+ CAN_ERR_PROT_LOC_CRC_DEL);
|
|
|
|
+ if (es->leaf.error_factor & M16C_EF_FORME)
|
|
|
|
+ cf->data[2] |= CAN_ERR_PROT_FORM;
|
|
|
|
+ if (es->leaf.error_factor & M16C_EF_STFE)
|
|
|
|
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
|
|
|
|
+ if (es->leaf.error_factor & M16C_EF_BITE0)
|
|
|
|
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
|
|
|
|
+ if (es->leaf.error_factor & M16C_EF_BITE1)
|
|
|
|
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
|
|
|
|
+ if (es->leaf.error_factor & M16C_EF_TRE)
|
|
|
|
+ cf->data[2] |= CAN_ERR_PROT_TX;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case KVASER_USBCAN:
|
|
|
|
+ if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) {
|
|
|
|
+ cf->can_id |= CAN_ERR_BUSERROR;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
|
|
- netif_carrier_off(priv->netdev);
|
|
|
|
|
|
+ cf->data[6] = es->txerr;
|
|
|
|
+ cf->data[7] = es->rxerr;
|
|
|
|
|
|
- new_state = CAN_STATE_BUS_OFF;
|
|
|
|
- } else if (status & M16C_STATE_BUS_PASSIVE) {
|
|
|
|
- if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
|
|
|
|
- cf->can_id |= CAN_ERR_CRTL;
|
|
|
|
-
|
|
|
|
- if (txerr || rxerr)
|
|
|
|
- cf->data[1] = (txerr > rxerr)
|
|
|
|
- ? CAN_ERR_CRTL_TX_PASSIVE
|
|
|
|
- : CAN_ERR_CRTL_RX_PASSIVE;
|
|
|
|
- else
|
|
|
|
- cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
|
|
|
|
- CAN_ERR_CRTL_RX_PASSIVE;
|
|
|
|
-
|
|
|
|
- priv->can.can_stats.error_passive++;
|
|
|
|
- }
|
|
|
|
|
|
+ stats->rx_packets++;
|
|
|
|
+ stats->rx_bytes += cf->can_dlc;
|
|
|
|
+ netif_rx(skb);
|
|
|
|
+}
|
|
|
|
|
|
- new_state = CAN_STATE_ERROR_PASSIVE;
|
|
|
|
- } else if (status & M16C_STATE_BUS_ERROR) {
|
|
|
|
- if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
|
|
|
|
- ((txerr >= 96) || (rxerr >= 96))) {
|
|
|
|
- cf->can_id |= CAN_ERR_CRTL;
|
|
|
|
- cf->data[1] = (txerr > rxerr)
|
|
|
|
- ? CAN_ERR_CRTL_TX_WARNING
|
|
|
|
- : CAN_ERR_CRTL_RX_WARNING;
|
|
|
|
-
|
|
|
|
- priv->can.can_stats.error_warning++;
|
|
|
|
- new_state = CAN_STATE_ERROR_WARNING;
|
|
|
|
- } else if ((priv->can.state > CAN_STATE_ERROR_ACTIVE) &&
|
|
|
|
- ((txerr < 96) && (rxerr < 96))) {
|
|
|
|
- cf->can_id |= CAN_ERR_PROT;
|
|
|
|
- cf->data[2] = CAN_ERR_PROT_ACTIVE;
|
|
|
|
|
|
+/* For USBCAN, report error to userspace iff the channels's errors counter
|
|
|
|
+ * has changed, or we're the only channel seeing a bus error state.
|
|
|
|
+ */
|
|
|
|
+static void kvaser_usbcan_conditionally_rx_error(const struct kvaser_usb *dev,
|
|
|
|
+ struct kvaser_usb_error_summary *es)
|
|
|
|
+{
|
|
|
|
+ struct kvaser_usb_net_priv *priv;
|
|
|
|
+ int channel;
|
|
|
|
+ bool report_error;
|
|
|
|
|
|
- new_state = CAN_STATE_ERROR_ACTIVE;
|
|
|
|
- }
|
|
|
|
|
|
+ channel = es->channel;
|
|
|
|
+ if (channel >= dev->nchannels) {
|
|
|
|
+ dev_err(dev->udev->dev.parent,
|
|
|
|
+ "Invalid channel number (%d)\n", channel);
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (!status) {
|
|
|
|
- cf->can_id |= CAN_ERR_PROT;
|
|
|
|
- cf->data[2] = CAN_ERR_PROT_ACTIVE;
|
|
|
|
|
|
+ priv = dev->nets[channel];
|
|
|
|
+ report_error = false;
|
|
|
|
|
|
- new_state = CAN_STATE_ERROR_ACTIVE;
|
|
|
|
|
|
+ if (es->txerr != priv->bec.txerr) {
|
|
|
|
+ es->usbcan.error_state |= USBCAN_ERROR_STATE_TX_ERROR;
|
|
|
|
+ report_error = true;
|
|
|
|
+ }
|
|
|
|
+ if (es->rxerr != priv->bec.rxerr) {
|
|
|
|
+ es->usbcan.error_state |= USBCAN_ERROR_STATE_RX_ERROR;
|
|
|
|
+ report_error = true;
|
|
|
|
+ }
|
|
|
|
+ if ((es->status & M16C_STATE_BUS_ERROR) &&
|
|
|
|
+ !(es->usbcan.other_ch_status & M16C_STATE_BUS_ERROR)) {
|
|
|
|
+ es->usbcan.error_state |= USBCAN_ERROR_STATE_BUSERROR;
|
|
|
|
+ report_error = true;
|
|
}
|
|
}
|
|
|
|
|
|
- if (priv->can.restart_ms &&
|
|
|
|
- (priv->can.state >= CAN_STATE_BUS_OFF) &&
|
|
|
|
- (new_state < CAN_STATE_BUS_OFF)) {
|
|
|
|
- cf->can_id |= CAN_ERR_RESTARTED;
|
|
|
|
- netif_carrier_on(priv->netdev);
|
|
|
|
|
|
+ if (report_error)
|
|
|
|
+ kvaser_usb_rx_error(dev, es);
|
|
|
|
+}
|
|
|
|
|
|
- priv->can.can_stats.restarts++;
|
|
|
|
- }
|
|
|
|
|
|
+static void kvaser_usbcan_rx_error(const struct kvaser_usb *dev,
|
|
|
|
+ const struct kvaser_msg *msg)
|
|
|
|
+{
|
|
|
|
+ struct kvaser_usb_error_summary es = { };
|
|
|
|
|
|
- if (error_factor) {
|
|
|
|
- priv->can.can_stats.bus_error++;
|
|
|
|
- stats->rx_errors++;
|
|
|
|
|
|
+ switch (msg->id) {
|
|
|
|
+ /* Sometimes errors are sent as unsolicited chip state events */
|
|
|
|
+ case CMD_CHIP_STATE_EVENT:
|
|
|
|
+ es.channel = msg->u.usbcan.chip_state_event.channel;
|
|
|
|
+ es.status = msg->u.usbcan.chip_state_event.status;
|
|
|
|
+ es.txerr = msg->u.usbcan.chip_state_event.tx_errors_count;
|
|
|
|
+ es.rxerr = msg->u.usbcan.chip_state_event.rx_errors_count;
|
|
|
|
+ kvaser_usbcan_conditionally_rx_error(dev, &es);
|
|
|
|
+ break;
|
|
|
|
|
|
- cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
|
|
|
|
-
|
|
|
|
- if (error_factor & M16C_EF_ACKE)
|
|
|
|
- cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
|
|
|
|
- if (error_factor & M16C_EF_CRCE)
|
|
|
|
- cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
|
|
|
|
- CAN_ERR_PROT_LOC_CRC_DEL);
|
|
|
|
- if (error_factor & M16C_EF_FORME)
|
|
|
|
- cf->data[2] |= CAN_ERR_PROT_FORM;
|
|
|
|
- if (error_factor & M16C_EF_STFE)
|
|
|
|
- cf->data[2] |= CAN_ERR_PROT_STUFF;
|
|
|
|
- if (error_factor & M16C_EF_BITE0)
|
|
|
|
- cf->data[2] |= CAN_ERR_PROT_BIT0;
|
|
|
|
- if (error_factor & M16C_EF_BITE1)
|
|
|
|
- cf->data[2] |= CAN_ERR_PROT_BIT1;
|
|
|
|
- if (error_factor & M16C_EF_TRE)
|
|
|
|
- cf->data[2] |= CAN_ERR_PROT_TX;
|
|
|
|
- }
|
|
|
|
|
|
+ case CMD_CAN_ERROR_EVENT:
|
|
|
|
+ es.channel = 0;
|
|
|
|
+ es.status = msg->u.usbcan.error_event.status_ch0;
|
|
|
|
+ es.txerr = msg->u.usbcan.error_event.tx_errors_count_ch0;
|
|
|
|
+ es.rxerr = msg->u.usbcan.error_event.rx_errors_count_ch0;
|
|
|
|
+ es.usbcan.other_ch_status =
|
|
|
|
+ msg->u.usbcan.error_event.status_ch1;
|
|
|
|
+ kvaser_usbcan_conditionally_rx_error(dev, &es);
|
|
|
|
+
|
|
|
|
+ /* The USBCAN firmware supports up to 2 channels.
|
|
|
|
+ * Now that ch0 was checked, check if ch1 has any errors.
|
|
|
|
+ */
|
|
|
|
+ if (dev->nchannels == MAX_USBCAN_NET_DEVICES) {
|
|
|
|
+ es.channel = 1;
|
|
|
|
+ es.status = msg->u.usbcan.error_event.status_ch1;
|
|
|
|
+ es.txerr = msg->u.usbcan.error_event.tx_errors_count_ch1;
|
|
|
|
+ es.rxerr = msg->u.usbcan.error_event.rx_errors_count_ch1;
|
|
|
|
+ es.usbcan.other_ch_status =
|
|
|
|
+ msg->u.usbcan.error_event.status_ch0;
|
|
|
|
+ kvaser_usbcan_conditionally_rx_error(dev, &es);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
|
|
- cf->data[6] = txerr;
|
|
|
|
- cf->data[7] = rxerr;
|
|
|
|
|
|
+ default:
|
|
|
|
+ dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
|
|
|
|
+ msg->id);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
- priv->bec.txerr = txerr;
|
|
|
|
- priv->bec.rxerr = rxerr;
|
|
|
|
|
|
+static void kvaser_leaf_rx_error(const struct kvaser_usb *dev,
|
|
|
|
+ const struct kvaser_msg *msg)
|
|
|
|
+{
|
|
|
|
+ struct kvaser_usb_error_summary es = { };
|
|
|
|
|
|
- priv->can.state = new_state;
|
|
|
|
|
|
+ switch (msg->id) {
|
|
|
|
+ case CMD_CAN_ERROR_EVENT:
|
|
|
|
+ es.channel = msg->u.leaf.error_event.channel;
|
|
|
|
+ es.status = msg->u.leaf.error_event.status;
|
|
|
|
+ es.txerr = msg->u.leaf.error_event.tx_errors_count;
|
|
|
|
+ es.rxerr = msg->u.leaf.error_event.rx_errors_count;
|
|
|
|
+ es.leaf.error_factor = msg->u.leaf.error_event.error_factor;
|
|
|
|
+ break;
|
|
|
|
+ case CMD_LEAF_LOG_MESSAGE:
|
|
|
|
+ es.channel = msg->u.leaf.log_message.channel;
|
|
|
|
+ es.status = msg->u.leaf.log_message.data[0];
|
|
|
|
+ es.txerr = msg->u.leaf.log_message.data[2];
|
|
|
|
+ es.rxerr = msg->u.leaf.log_message.data[3];
|
|
|
|
+ es.leaf.error_factor = msg->u.leaf.log_message.data[1];
|
|
|
|
+ break;
|
|
|
|
+ case CMD_CHIP_STATE_EVENT:
|
|
|
|
+ es.channel = msg->u.leaf.chip_state_event.channel;
|
|
|
|
+ es.status = msg->u.leaf.chip_state_event.status;
|
|
|
|
+ es.txerr = msg->u.leaf.chip_state_event.tx_errors_count;
|
|
|
|
+ es.rxerr = msg->u.leaf.chip_state_event.rx_errors_count;
|
|
|
|
+ es.leaf.error_factor = 0;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
|
|
|
|
+ msg->id);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
- stats->rx_packets++;
|
|
|
|
- stats->rx_bytes += cf->can_dlc;
|
|
|
|
- netif_rx(skb);
|
|
|
|
|
|
+ kvaser_usb_rx_error(dev, &es);
|
|
}
|
|
}
|
|
|
|
|
|
static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
|
|
static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
|
|
@@ -776,16 +1089,19 @@ static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
struct net_device_stats *stats = &priv->netdev->stats;
|
|
struct net_device_stats *stats = &priv->netdev->stats;
|
|
|
|
|
|
- if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
|
|
|
|
|
|
+ if (msg->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME |
|
|
MSG_FLAG_NERR)) {
|
|
MSG_FLAG_NERR)) {
|
|
netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
|
|
netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
|
|
- msg->u.rx_can.flag);
|
|
|
|
|
|
+ msg->u.rx_can_header.flag);
|
|
|
|
|
|
stats->rx_errors++;
|
|
stats->rx_errors++;
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
|
|
|
|
|
|
+ if (msg->u.rx_can_header.flag & MSG_FLAG_OVERRUN) {
|
|
|
|
+ stats->rx_over_errors++;
|
|
|
|
+ stats->rx_errors++;
|
|
|
|
+
|
|
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++;
|
|
@@ -795,9 +1111,6 @@ static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
|
|
cf->can_id |= CAN_ERR_CRTL;
|
|
cf->can_id |= CAN_ERR_CRTL;
|
|
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
|
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
|
|
|
|
|
- stats->rx_over_errors++;
|
|
|
|
- stats->rx_errors++;
|
|
|
|
-
|
|
|
|
stats->rx_packets++;
|
|
stats->rx_packets++;
|
|
stats->rx_bytes += cf->can_dlc;
|
|
stats->rx_bytes += cf->can_dlc;
|
|
netif_rx(skb);
|
|
netif_rx(skb);
|
|
@@ -811,7 +1124,8 @@ static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
|
|
struct can_frame *cf;
|
|
struct can_frame *cf;
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
struct net_device_stats *stats;
|
|
struct net_device_stats *stats;
|
|
- u8 channel = msg->u.rx_can.channel;
|
|
|
|
|
|
+ u8 channel = msg->u.rx_can_header.channel;
|
|
|
|
+ const u8 *rx_msg = NULL; /* GCC */
|
|
|
|
|
|
if (channel >= dev->nchannels) {
|
|
if (channel >= dev->nchannels) {
|
|
dev_err(dev->udev->dev.parent,
|
|
dev_err(dev->udev->dev.parent,
|
|
@@ -822,60 +1136,68 @@ static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
|
|
priv = dev->nets[channel];
|
|
priv = dev->nets[channel];
|
|
stats = &priv->netdev->stats;
|
|
stats = &priv->netdev->stats;
|
|
|
|
|
|
- if ((msg->u.rx_can.flag & MSG_FLAG_ERROR_FRAME) &&
|
|
|
|
- (msg->id == CMD_LOG_MESSAGE)) {
|
|
|
|
- kvaser_usb_rx_error(dev, msg);
|
|
|
|
|
|
+ if ((msg->u.rx_can_header.flag & MSG_FLAG_ERROR_FRAME) &&
|
|
|
|
+ (dev->family == KVASER_LEAF && msg->id == CMD_LEAF_LOG_MESSAGE)) {
|
|
|
|
+ kvaser_leaf_rx_error(dev, msg);
|
|
return;
|
|
return;
|
|
- } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
|
|
|
|
- MSG_FLAG_NERR |
|
|
|
|
- MSG_FLAG_OVERRUN)) {
|
|
|
|
|
|
+ } else if (msg->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME |
|
|
|
|
+ MSG_FLAG_NERR |
|
|
|
|
+ MSG_FLAG_OVERRUN)) {
|
|
kvaser_usb_rx_can_err(priv, msg);
|
|
kvaser_usb_rx_can_err(priv, msg);
|
|
return;
|
|
return;
|
|
- } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) {
|
|
|
|
|
|
+ } else if (msg->u.rx_can_header.flag & ~MSG_FLAG_REMOTE_FRAME) {
|
|
netdev_warn(priv->netdev,
|
|
netdev_warn(priv->netdev,
|
|
"Unhandled frame (flags: 0x%02x)",
|
|
"Unhandled frame (flags: 0x%02x)",
|
|
- msg->u.rx_can.flag);
|
|
|
|
|
|
+ msg->u.rx_can_header.flag);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ switch (dev->family) {
|
|
|
|
+ case KVASER_LEAF:
|
|
|
|
+ rx_msg = msg->u.leaf.rx_can.msg;
|
|
|
|
+ break;
|
|
|
|
+ case KVASER_USBCAN:
|
|
|
|
+ rx_msg = msg->u.usbcan.rx_can.msg;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
skb = alloc_can_skb(priv->netdev, &cf);
|
|
skb = alloc_can_skb(priv->netdev, &cf);
|
|
if (!skb) {
|
|
if (!skb) {
|
|
stats->tx_dropped++;
|
|
stats->tx_dropped++;
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (msg->id == CMD_LOG_MESSAGE) {
|
|
|
|
- cf->can_id = le32_to_cpu(msg->u.log_message.id);
|
|
|
|
|
|
+ if (dev->family == KVASER_LEAF && msg->id == CMD_LEAF_LOG_MESSAGE) {
|
|
|
|
+ cf->can_id = le32_to_cpu(msg->u.leaf.log_message.id);
|
|
if (cf->can_id & KVASER_EXTENDED_FRAME)
|
|
if (cf->can_id & KVASER_EXTENDED_FRAME)
|
|
cf->can_id &= CAN_EFF_MASK | CAN_EFF_FLAG;
|
|
cf->can_id &= CAN_EFF_MASK | CAN_EFF_FLAG;
|
|
else
|
|
else
|
|
cf->can_id &= CAN_SFF_MASK;
|
|
cf->can_id &= CAN_SFF_MASK;
|
|
|
|
|
|
- cf->can_dlc = get_can_dlc(msg->u.log_message.dlc);
|
|
|
|
|
|
+ cf->can_dlc = get_can_dlc(msg->u.leaf.log_message.dlc);
|
|
|
|
|
|
- if (msg->u.log_message.flags & MSG_FLAG_REMOTE_FRAME)
|
|
|
|
|
|
+ if (msg->u.leaf.log_message.flags & MSG_FLAG_REMOTE_FRAME)
|
|
cf->can_id |= CAN_RTR_FLAG;
|
|
cf->can_id |= CAN_RTR_FLAG;
|
|
else
|
|
else
|
|
- memcpy(cf->data, &msg->u.log_message.data,
|
|
|
|
|
|
+ memcpy(cf->data, &msg->u.leaf.log_message.data,
|
|
cf->can_dlc);
|
|
cf->can_dlc);
|
|
} else {
|
|
} else {
|
|
- cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
|
|
|
|
- (msg->u.rx_can.msg[1] & 0x3f);
|
|
|
|
|
|
+ cf->can_id = ((rx_msg[0] & 0x1f) << 6) | (rx_msg[1] & 0x3f);
|
|
|
|
|
|
if (msg->id == CMD_RX_EXT_MESSAGE) {
|
|
if (msg->id == CMD_RX_EXT_MESSAGE) {
|
|
cf->can_id <<= 18;
|
|
cf->can_id <<= 18;
|
|
- cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
|
|
|
|
- ((msg->u.rx_can.msg[3] & 0xff) << 6) |
|
|
|
|
- (msg->u.rx_can.msg[4] & 0x3f);
|
|
|
|
|
|
+ cf->can_id |= ((rx_msg[2] & 0x0f) << 14) |
|
|
|
|
+ ((rx_msg[3] & 0xff) << 6) |
|
|
|
|
+ (rx_msg[4] & 0x3f);
|
|
cf->can_id |= CAN_EFF_FLAG;
|
|
cf->can_id |= CAN_EFF_FLAG;
|
|
}
|
|
}
|
|
|
|
|
|
- cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
|
|
|
|
|
|
+ cf->can_dlc = get_can_dlc(rx_msg[5]);
|
|
|
|
|
|
- if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME)
|
|
|
|
|
|
+ if (msg->u.rx_can_header.flag & MSG_FLAG_REMOTE_FRAME)
|
|
cf->can_id |= CAN_RTR_FLAG;
|
|
cf->can_id |= CAN_RTR_FLAG;
|
|
else
|
|
else
|
|
- memcpy(cf->data, &msg->u.rx_can.msg[6],
|
|
|
|
|
|
+ memcpy(cf->data, &rx_msg[6],
|
|
cf->can_dlc);
|
|
cf->can_dlc);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -938,21 +1260,35 @@ static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
|
|
|
|
|
|
case CMD_RX_STD_MESSAGE:
|
|
case CMD_RX_STD_MESSAGE:
|
|
case CMD_RX_EXT_MESSAGE:
|
|
case CMD_RX_EXT_MESSAGE:
|
|
- case CMD_LOG_MESSAGE:
|
|
|
|
|
|
+ kvaser_usb_rx_can_msg(dev, msg);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case CMD_LEAF_LOG_MESSAGE:
|
|
|
|
+ if (dev->family != KVASER_LEAF)
|
|
|
|
+ goto warn;
|
|
kvaser_usb_rx_can_msg(dev, msg);
|
|
kvaser_usb_rx_can_msg(dev, msg);
|
|
break;
|
|
break;
|
|
|
|
|
|
case CMD_CHIP_STATE_EVENT:
|
|
case CMD_CHIP_STATE_EVENT:
|
|
case CMD_CAN_ERROR_EVENT:
|
|
case CMD_CAN_ERROR_EVENT:
|
|
- kvaser_usb_rx_error(dev, msg);
|
|
|
|
|
|
+ if (dev->family == KVASER_LEAF)
|
|
|
|
+ kvaser_leaf_rx_error(dev, msg);
|
|
|
|
+ else
|
|
|
|
+ kvaser_usbcan_rx_error(dev, msg);
|
|
break;
|
|
break;
|
|
|
|
|
|
case CMD_TX_ACKNOWLEDGE:
|
|
case CMD_TX_ACKNOWLEDGE:
|
|
kvaser_usb_tx_acknowledge(dev, msg);
|
|
kvaser_usb_tx_acknowledge(dev, msg);
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+ /* Ignored messages */
|
|
|
|
+ case CMD_USBCAN_CLOCK_OVERFLOW_EVENT:
|
|
|
|
+ if (dev->family != KVASER_USBCAN)
|
|
|
|
+ goto warn;
|
|
|
|
+ break;
|
|
|
|
+
|
|
default:
|
|
default:
|
|
- dev_warn(dev->udev->dev.parent,
|
|
|
|
|
|
+warn: dev_warn(dev->udev->dev.parent,
|
|
"Unhandled message (%d)\n", msg->id);
|
|
"Unhandled message (%d)\n", msg->id);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -1172,7 +1508,7 @@ static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
|
|
dev->rxbuf[i],
|
|
dev->rxbuf[i],
|
|
dev->rxbuf_dma[i]);
|
|
dev->rxbuf_dma[i]);
|
|
|
|
|
|
- for (i = 0; i < MAX_NET_DEVICES; i++) {
|
|
|
|
|
|
+ for (i = 0; i < dev->nchannels; i++) {
|
|
struct kvaser_usb_net_priv *priv = dev->nets[i];
|
|
struct kvaser_usb_net_priv *priv = dev->nets[i];
|
|
|
|
|
|
if (priv)
|
|
if (priv)
|
|
@@ -1280,6 +1616,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
|
|
struct kvaser_msg *msg;
|
|
struct kvaser_msg *msg;
|
|
int i, err;
|
|
int i, err;
|
|
int ret = NETDEV_TX_OK;
|
|
int ret = NETDEV_TX_OK;
|
|
|
|
+ u8 *msg_tx_can_flags = NULL; /* GCC */
|
|
|
|
|
|
if (can_dropped_invalid_skb(netdev, skb))
|
|
if (can_dropped_invalid_skb(netdev, skb))
|
|
return NETDEV_TX_OK;
|
|
return NETDEV_TX_OK;
|
|
@@ -1301,9 +1638,19 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
|
|
|
|
|
|
msg = buf;
|
|
msg = buf;
|
|
msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
|
|
msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
|
|
- msg->u.tx_can.flags = 0;
|
|
|
|
msg->u.tx_can.channel = priv->channel;
|
|
msg->u.tx_can.channel = priv->channel;
|
|
|
|
|
|
|
|
+ switch (dev->family) {
|
|
|
|
+ case KVASER_LEAF:
|
|
|
|
+ msg_tx_can_flags = &msg->u.tx_can.leaf.flags;
|
|
|
|
+ break;
|
|
|
|
+ case KVASER_USBCAN:
|
|
|
|
+ msg_tx_can_flags = &msg->u.tx_can.usbcan.flags;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ *msg_tx_can_flags = 0;
|
|
|
|
+
|
|
if (cf->can_id & CAN_EFF_FLAG) {
|
|
if (cf->can_id & CAN_EFF_FLAG) {
|
|
msg->id = CMD_TX_EXT_MESSAGE;
|
|
msg->id = CMD_TX_EXT_MESSAGE;
|
|
msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
|
|
msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
|
|
@@ -1321,7 +1668,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
|
|
memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
|
|
memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
|
|
|
|
|
|
if (cf->can_id & CAN_RTR_FLAG)
|
|
if (cf->can_id & CAN_RTR_FLAG)
|
|
- msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
|
|
|
|
|
|
+ *msg_tx_can_flags |= MSG_FLAG_REMOTE_FRAME;
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
|
|
for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
|
|
if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
|
|
if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
|
|
@@ -1590,6 +1937,17 @@ static int kvaser_usb_probe(struct usb_interface *intf,
|
|
if (!dev)
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
+ if (kvaser_is_leaf(id)) {
|
|
|
|
+ dev->family = KVASER_LEAF;
|
|
|
|
+ } else if (kvaser_is_usbcan(id)) {
|
|
|
|
+ dev->family = KVASER_USBCAN;
|
|
|
|
+ } else {
|
|
|
|
+ dev_err(&intf->dev,
|
|
|
|
+ "Product ID (%d) does not belong to any known Kvaser USB family",
|
|
|
|
+ id->idProduct);
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
err = kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
|
|
err = kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
|
|
if (err) {
|
|
if (err) {
|
|
dev_err(&intf->dev, "Cannot get usb endpoint(s)");
|
|
dev_err(&intf->dev, "Cannot get usb endpoint(s)");
|