mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 13:51:44 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next
Marc Kleine-Budde says: ==================== this is a pull request of 29 patches for net-next/master. The first patch is by Daniel S. Trevitz and adds documentation for switchable termination resistors. Zhang Changzhong's patch fixes a debug output in the j13939 stack. Oliver Hartkopp finally removes the pch_can driver, which is superseded by the generic c_can driver. Gustavo A. R. Silva replaces a zero-length array with DECLARE_FLEX_ARRAY() in the ucan driver. Kees Cook's patch removes a no longer needed silencing of "-Warray-bounds" warnings for the kvaser_usb driver. The next 2 patches target the m_can driver. The first is by me cleans up the LEC error handling, the second is by Vivek Yadav and extends the LEC error handling to the data phase of CAN-FD frames. The next 9 patches all target the gs_usb driver. The first 5 patches are by me and improve the Kconfig prompt and help text, set netdev->dev_id to distinguish multi CAN channel devices, allow loopback and listen only at the same time, and clean up the gs_can_open() function a bit. The remaining 4 patches are by Jeroen Hofstee and add support for 2 new features: Bus Error Reporting and Get State. Jimmy Assarsson and Anssi Hannula contribute 10 patches for the kvaser_usb driver. They first add Listen Only and Bus Error Reporting support, handle CMD_ERROR_EVENT errors, improve CAN state handling, restart events, and configuration of the bit timing parameters. Another patch by me which fixes the indention in the m_can driver. A patch by Dongliang Mu cleans up the ucan_disconnect() function in the ucan driver. The last patch by Biju Das is for the rcan_canfd driver and cleans up the reset handling. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
9c8dddab2a
@ -1148,6 +1148,39 @@ tuning on deep embedded systems'. The author is running a MPC603e
|
||||
load without any problems ...
|
||||
|
||||
|
||||
Switchable Termination Resistors
|
||||
--------------------------------
|
||||
|
||||
CAN bus requires a specific impedance across the differential pair,
|
||||
typically provided by two 120Ohm resistors on the farthest nodes of
|
||||
the bus. Some CAN controllers support activating / deactivating a
|
||||
termination resistor(s) to provide the correct impedance.
|
||||
|
||||
Query the available resistances::
|
||||
|
||||
$ ip -details link show can0
|
||||
...
|
||||
termination 120 [ 0, 120 ]
|
||||
|
||||
Activate the terminating resistor::
|
||||
|
||||
$ ip link set dev can0 type can termination 120
|
||||
|
||||
Deactivate the terminating resistor::
|
||||
|
||||
$ ip link set dev can0 type can termination 0
|
||||
|
||||
To enable termination resistor support to a can-controller, either
|
||||
implement in the controller's struct can-priv::
|
||||
|
||||
termination_const
|
||||
termination_const_cnt
|
||||
do_set_termination
|
||||
|
||||
or add gpio control with the device tree entries from
|
||||
Documentation/devicetree/bindings/net/can/can-controller.yaml
|
||||
|
||||
|
||||
The Virtual CAN Driver (vcan)
|
||||
-----------------------------
|
||||
|
||||
|
@ -198,14 +198,6 @@ config CAN_XILINXCAN
|
||||
Xilinx CAN driver. This driver supports both soft AXI CAN IP and
|
||||
Zynq CANPS IP.
|
||||
|
||||
config PCH_CAN
|
||||
tristate "Intel EG20T PCH CAN controller"
|
||||
depends on PCI && (X86_32 || COMPILE_TEST)
|
||||
help
|
||||
This driver is for PCH CAN of Topcliff (Intel EG20T PCH) which
|
||||
is an IOH for x86 embedded processor (Intel Atom E6xx series).
|
||||
This driver can access CAN bus.
|
||||
|
||||
source "drivers/net/can/c_can/Kconfig"
|
||||
source "drivers/net/can/cc770/Kconfig"
|
||||
source "drivers/net/can/ctucanfd/Kconfig"
|
||||
|
@ -30,6 +30,5 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/
|
||||
obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
|
||||
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
|
||||
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
|
||||
obj-$(CONFIG_PCH_CAN) += pch_can.o
|
||||
|
||||
subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
|
||||
|
@ -20,5 +20,6 @@ config CAN_C_CAN_PCI
|
||||
depends on PCI
|
||||
help
|
||||
This driver adds support for the C_CAN/D_CAN chips connected
|
||||
to the PCI bus.
|
||||
to the PCI bus. E.g. for the C_CAN controller IP inside the
|
||||
Intel Atom E6xx series IOH (aka EG20T 'PCH CAN').
|
||||
endif
|
||||
|
@ -156,6 +156,7 @@ enum m_can_reg {
|
||||
#define PSR_EW BIT(6)
|
||||
#define PSR_EP BIT(5)
|
||||
#define PSR_LEC_MASK GENMASK(2, 0)
|
||||
#define PSR_DLEC_MASK GENMASK(10, 8)
|
||||
|
||||
/* Interrupt Register (IR) */
|
||||
#define IR_ALL_INT 0xffffffff
|
||||
@ -209,7 +210,7 @@ enum m_can_reg {
|
||||
|
||||
/* Interrupts for version >= 3.1.x */
|
||||
#define IR_ERR_LEC_31X (IR_PED | IR_PEA)
|
||||
#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_BEU | IR_BEC | \
|
||||
#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_BEU | IR_BEC | \
|
||||
IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | IR_RF1L | \
|
||||
IR_RF0L)
|
||||
#define IR_ERR_ALL_31X (IR_ERR_STATE | IR_ERR_BUS_31X)
|
||||
@ -816,11 +817,9 @@ static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus)
|
||||
netdev_err(dev, "Message RAM access failure occurred\n");
|
||||
}
|
||||
|
||||
static inline bool is_lec_err(u32 psr)
|
||||
static inline bool is_lec_err(u8 lec)
|
||||
{
|
||||
psr &= LEC_UNUSED;
|
||||
|
||||
return psr && (psr != LEC_UNUSED);
|
||||
return lec != LEC_NO_ERROR && lec != LEC_NO_CHANGE;
|
||||
}
|
||||
|
||||
static inline bool m_can_is_protocol_err(u32 irqstatus)
|
||||
@ -875,9 +874,20 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
|
||||
work_done += m_can_handle_lost_msg(dev);
|
||||
|
||||
/* handle lec errors on the bus */
|
||||
if ((cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) &&
|
||||
is_lec_err(psr))
|
||||
work_done += m_can_handle_lec_err(dev, psr & LEC_UNUSED);
|
||||
if (cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
|
||||
u8 lec = FIELD_GET(PSR_LEC_MASK, psr);
|
||||
u8 dlec = FIELD_GET(PSR_DLEC_MASK, psr);
|
||||
|
||||
if (is_lec_err(lec)) {
|
||||
netdev_dbg(dev, "Arbitration phase error detected\n");
|
||||
work_done += m_can_handle_lec_err(dev, lec);
|
||||
}
|
||||
|
||||
if (is_lec_err(dlec)) {
|
||||
netdev_dbg(dev, "Data phase error detected\n");
|
||||
work_done += m_can_handle_lec_err(dev, dlec);
|
||||
}
|
||||
}
|
||||
|
||||
/* handle protocol errors in arbitration phase */
|
||||
if ((cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) &&
|
||||
|
@ -38,7 +38,7 @@ enum m_can_lec_type {
|
||||
LEC_BIT1_ERROR,
|
||||
LEC_BIT0_ERROR,
|
||||
LEC_CRC_ERROR,
|
||||
LEC_UNUSED,
|
||||
LEC_NO_CHANGE,
|
||||
};
|
||||
|
||||
enum m_can_mram_cfg {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1889,17 +1889,17 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
gpriv->chip_id = chip_id;
|
||||
gpriv->max_channels = max_channels;
|
||||
|
||||
if (gpriv->chip_id == RENESAS_RZG2L) {
|
||||
gpriv->rstc1 = devm_reset_control_get_exclusive(&pdev->dev, "rstp_n");
|
||||
if (IS_ERR(gpriv->rstc1))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc1),
|
||||
"failed to get rstp_n\n");
|
||||
gpriv->rstc1 = devm_reset_control_get_optional_exclusive(&pdev->dev,
|
||||
"rstp_n");
|
||||
if (IS_ERR(gpriv->rstc1))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc1),
|
||||
"failed to get rstp_n\n");
|
||||
|
||||
gpriv->rstc2 = devm_reset_control_get_exclusive(&pdev->dev, "rstc_n");
|
||||
if (IS_ERR(gpriv->rstc2))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc2),
|
||||
"failed to get rstc_n\n");
|
||||
}
|
||||
gpriv->rstc2 = devm_reset_control_get_optional_exclusive(&pdev->dev,
|
||||
"rstc_n");
|
||||
if (IS_ERR(gpriv->rstc2))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc2),
|
||||
"failed to get rstc_n\n");
|
||||
|
||||
/* Peripheral clock */
|
||||
gpriv->clkp = devm_clk_get(&pdev->dev, "fck");
|
||||
|
@ -38,10 +38,13 @@ config CAN_ETAS_ES58X
|
||||
will be called etas_es58x.
|
||||
|
||||
config CAN_GS_USB
|
||||
tristate "Geschwister Schneider UG interfaces"
|
||||
tristate "Geschwister Schneider UG and candleLight compatible interfaces"
|
||||
help
|
||||
This driver supports the Geschwister Schneider and bytewerk.org
|
||||
candleLight USB CAN interfaces USB/CAN devices
|
||||
This driver supports the Geschwister Schneider and
|
||||
bytewerk.org candleLight compatible
|
||||
(https://github.com/candle-usb/candleLight_fw) USB/CAN
|
||||
interfaces.
|
||||
|
||||
If unsure choose N,
|
||||
choose Y for built in support,
|
||||
M to compile as module (module will be named: gs_usb).
|
||||
|
@ -66,6 +66,7 @@ enum gs_usb_breq {
|
||||
GS_USB_BREQ_BT_CONST_EXT,
|
||||
GS_USB_BREQ_SET_TERMINATION,
|
||||
GS_USB_BREQ_GET_TERMINATION,
|
||||
GS_USB_BREQ_GET_STATE,
|
||||
};
|
||||
|
||||
enum gs_can_mode {
|
||||
@ -134,6 +135,8 @@ struct gs_device_config {
|
||||
/* GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9) */
|
||||
/* GS_CAN_FEATURE_BT_CONST_EXT BIT(10) */
|
||||
/* GS_CAN_FEATURE_TERMINATION BIT(11) */
|
||||
#define GS_CAN_MODE_BERR_REPORTING BIT(12)
|
||||
/* GS_CAN_FEATURE_GET_STATE BIT(13) */
|
||||
|
||||
struct gs_device_mode {
|
||||
__le32 mode;
|
||||
@ -174,7 +177,9 @@ struct gs_device_termination_state {
|
||||
#define GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9)
|
||||
#define GS_CAN_FEATURE_BT_CONST_EXT BIT(10)
|
||||
#define GS_CAN_FEATURE_TERMINATION BIT(11)
|
||||
#define GS_CAN_FEATURE_MASK GENMASK(11, 0)
|
||||
#define GS_CAN_FEATURE_BERR_REPORTING BIT(12)
|
||||
#define GS_CAN_FEATURE_GET_STATE BIT(13)
|
||||
#define GS_CAN_FEATURE_MASK GENMASK(13, 0)
|
||||
|
||||
/* internal quirks - keep in GS_CAN_FEATURE space for now */
|
||||
|
||||
@ -843,8 +848,6 @@ static int gs_can_open(struct net_device *netdev)
|
||||
|
||||
ctrlmode = dev->can.ctrlmode;
|
||||
if (ctrlmode & CAN_CTRLMODE_FD) {
|
||||
flags |= GS_CAN_MODE_FD;
|
||||
|
||||
if (dev->feature & GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX)
|
||||
dev->hf_size_tx = struct_size(hf, canfd_quirk, 1);
|
||||
else
|
||||
@ -911,25 +914,29 @@ static int gs_can_open(struct net_device *netdev)
|
||||
/* flags */
|
||||
if (ctrlmode & CAN_CTRLMODE_LOOPBACK)
|
||||
flags |= GS_CAN_MODE_LOOP_BACK;
|
||||
else if (ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
||||
flags |= GS_CAN_MODE_LISTEN_ONLY;
|
||||
|
||||
/* Controller is not allowed to retry TX
|
||||
* this mode is unavailable on atmels uc3c hardware
|
||||
*/
|
||||
if (ctrlmode & CAN_CTRLMODE_ONE_SHOT)
|
||||
flags |= GS_CAN_MODE_ONE_SHOT;
|
||||
if (ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
||||
flags |= GS_CAN_MODE_LISTEN_ONLY;
|
||||
|
||||
if (ctrlmode & CAN_CTRLMODE_3_SAMPLES)
|
||||
flags |= GS_CAN_MODE_TRIPLE_SAMPLE;
|
||||
|
||||
if (ctrlmode & CAN_CTRLMODE_ONE_SHOT)
|
||||
flags |= GS_CAN_MODE_ONE_SHOT;
|
||||
|
||||
if (ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
|
||||
flags |= GS_CAN_MODE_BERR_REPORTING;
|
||||
|
||||
if (ctrlmode & CAN_CTRLMODE_FD)
|
||||
flags |= GS_CAN_MODE_FD;
|
||||
|
||||
/* if hardware supports timestamps, enable it */
|
||||
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
|
||||
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) {
|
||||
flags |= GS_CAN_MODE_HW_TIMESTAMP;
|
||||
|
||||
/* start polling timestamp */
|
||||
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
|
||||
/* start polling timestamp */
|
||||
gs_usb_timestamp_init(dev);
|
||||
}
|
||||
|
||||
/* finally start device */
|
||||
dev->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
@ -954,6 +961,42 @@ static int gs_can_open(struct net_device *netdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gs_usb_get_state(const struct net_device *netdev,
|
||||
struct can_berr_counter *bec,
|
||||
enum can_state *state)
|
||||
{
|
||||
struct gs_can *dev = netdev_priv(netdev);
|
||||
struct gs_device_state ds;
|
||||
int rc;
|
||||
|
||||
rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0,
|
||||
GS_USB_BREQ_GET_STATE,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
dev->channel, 0,
|
||||
&ds, sizeof(ds),
|
||||
USB_CTRL_GET_TIMEOUT,
|
||||
GFP_KERNEL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (le32_to_cpu(ds.state) >= CAN_STATE_MAX)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
*state = le32_to_cpu(ds.state);
|
||||
bec->txerr = le32_to_cpu(ds.txerr);
|
||||
bec->rxerr = le32_to_cpu(ds.rxerr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gs_usb_can_get_berr_counter(const struct net_device *netdev,
|
||||
struct can_berr_counter *bec)
|
||||
{
|
||||
enum can_state state;
|
||||
|
||||
return gs_usb_get_state(netdev, bec, &state);
|
||||
}
|
||||
|
||||
static int gs_can_close(struct net_device *netdev)
|
||||
{
|
||||
int rc;
|
||||
@ -1153,6 +1196,7 @@ static struct gs_can *gs_make_candev(unsigned int channel,
|
||||
netdev->ethtool_ops = &gs_usb_ethtool_ops;
|
||||
|
||||
netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */
|
||||
netdev->dev_id = channel;
|
||||
|
||||
/* dev setup */
|
||||
strcpy(dev->bt_const.name, KBUILD_MODNAME);
|
||||
@ -1224,6 +1268,12 @@ static struct gs_can *gs_make_candev(unsigned int channel,
|
||||
}
|
||||
}
|
||||
|
||||
if (feature & GS_CAN_FEATURE_BERR_REPORTING)
|
||||
dev->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING;
|
||||
|
||||
if (feature & GS_CAN_FEATURE_GET_STATE)
|
||||
dev->can.do_get_berr_counter = gs_usb_can_get_berr_counter;
|
||||
|
||||
/* The CANtact Pro from LinkLayer Labs is based on the
|
||||
* LPC54616 µC, which is affected by the NXP LPC USB transfer
|
||||
* erratum. However, the current firmware (version 2) doesn't
|
||||
|
@ -1,8 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
|
||||
kvaser_usb-y = kvaser_usb_core.o kvaser_usb_leaf.o kvaser_usb_hydra.o
|
||||
|
||||
# FIXME: temporarily silence -Warray-bounds on non W=1+ builds
|
||||
ifndef KBUILD_EXTRA_WARN
|
||||
CFLAGS_kvaser_usb_hydra.o += -Wno-array-bounds
|
||||
endif
|
||||
|
@ -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,
|
||||
|
@ -245,7 +245,8 @@ struct ucan_message_in {
|
||||
/* CAN transmission complete
|
||||
* (type == UCAN_IN_TX_COMPLETE)
|
||||
*/
|
||||
struct ucan_tx_complete_entry_t can_tx_complete_msg[0];
|
||||
DECLARE_FLEX_ARRAY(struct ucan_tx_complete_entry_t,
|
||||
can_tx_complete_msg);
|
||||
} __aligned(0x4) msg;
|
||||
} __packed __aligned(0x4);
|
||||
|
||||
@ -1581,7 +1582,7 @@ static void ucan_disconnect(struct usb_interface *intf)
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
if (up) {
|
||||
unregister_netdev(up->netdev);
|
||||
unregister_candev(up->netdev);
|
||||
free_candev(up->netdev);
|
||||
}
|
||||
}
|
||||
|
@ -985,7 +985,7 @@ static int j1939_session_tx_eoma(struct j1939_session *session)
|
||||
/* wait for the EOMA packet to come in */
|
||||
j1939_tp_set_rxtimeout(session, 1250);
|
||||
|
||||
netdev_dbg(session->priv->ndev, "%p: 0x%p\n", __func__, session);
|
||||
netdev_dbg(session->priv->ndev, "%s: 0x%p\n", __func__, session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user