Linux CAN drivers development
 help / color / mirror / Atom feed
* [PATCH] Fixed status entry in m_can documentation
From: Vyacheslav V. Yurkov @ 2016-12-22 10:45 UTC (permalink / raw)
  To: linux-can-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Jiri Kosina
  Cc: Wolfgang Grandegger, Marc Kleine-Budde, Rob Herring, Mark Rutland,
	Vyacheslav V. Yurkov

Use valid value for 'enabled' in status field

Signed-off-by: Vyacheslav V. Yurkov <uvv.mail-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 Documentation/devicetree/bindings/net/can/m_can.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/net/can/m_can.txt b/Documentation/devicetree/bindings/net/can/m_can.txt
index 9e33177..5facaf5 100644
--- a/Documentation/devicetree/bindings/net/can/m_can.txt
+++ b/Documentation/devicetree/bindings/net/can/m_can.txt
@@ -63,5 +63,5 @@ Board dts:
 &m_can1 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_m_can1>;
-	status = "enabled";
+	status = "okay";
 };
-- 
2.9.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* RE: CAN Termination API - was Re: Adding new CAN driver
From: Ramesh Shanmugasundaram @ 2016-12-22  9:39 UTC (permalink / raw)
  To: Oliver Hartkopp, Kurt Van Dijck
  Cc: Kołłątaj, Remigiusz, Alexander Stein,
	Marc Kleine-Budde, Uwe Bonnes, linux-can@vger.kernel.org
In-Reply-To: <18d3932b-8c74-a470-661d-7f2ee1a9f2f5@hartkopp.net>

> Subject: CAN Termination API - was Re: Adding new CAN driver
> 
> Hi all,
> 
> I would like to pick up the termination API again.
> 
> >>> +
> >>> +    /* array of supported discrete resistor values (Ohm) */
> >>> +    __u16 term[8];
> >
> > Do you think 8 resistor values are enough?
> >
> 
> see below ...
> 
> >>> +};
> >>> +
> >>> +/*
> >>> + * CAN termination
> >>> + */
> >>> +struct can_termination {
> >>> +    __u16 term;
> >>> +};
> >>> +
> >>> +#define CAN_RESISTOR_INVALID    0x0000U    /* indicates no value /
> >>> unsupported */
> >>> +#define CAN_RESISTOR_DISABLED    0xFFFFU    /* indicates disabled
> >>> termination */
> >>> +
> >>>   #define CAN_CTRLMODE_LOOPBACK        0x01    /* Loopback mode */
> >>>   #define CAN_CTRLMODE_LISTENONLY        0x02    /* Listen-only mode
> */
> >>>   #define CAN_CTRLMODE_3_SAMPLES        0x04    /* Triple sampling
> >>> mode */
> >>> @@ -127,6 +153,8 @@ enum {
> >>>       IFLA_CAN_BERR_COUNTER,
> >>>       IFLA_CAN_DATA_BITTIMING,
> >>>       IFLA_CAN_DATA_BITTIMING_CONST,
> >>> +    IFLA_CAN_TERMINATION,
> >>> +    IFLA_CAN_TERMINATION_CONST,
> >>>       __IFLA_CAN_MAX
> >>>   };
> >>>
> >>> With this interface the ip tool can get the capabilities of the
> >>> termination analogue to the BITTIMING_CONST values.
> >>> And it can set the termination with IFLA_CAN_TERMINATION.
> >>
> >> ACK.
> 
> I tried to get into the netlink stuff and I wonder how to make this API
> really simple.
> 
> By now the netlink interface in linux/drivers/net/can/dev.c looks like:
> 
> /*
>   * CAN netlink interface
>   */
> static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
>          [IFLA_CAN_STATE]        = { .type = NLA_U32 },
>          [IFLA_CAN_CTRLMODE]     = { .len = sizeof(struct can_ctrlmode) },
>          [IFLA_CAN_RESTART_MS]   = { .type = NLA_U32 },
>          [IFLA_CAN_RESTART]      = { .type = NLA_U32 },
>          [IFLA_CAN_BITTIMING]    = { .len = sizeof(struct can_bittiming)
> },
>          [IFLA_CAN_BITTIMING_CONST]
>                                  = { .len = sizeof(struct
> can_bittiming_const) },
>          [IFLA_CAN_CLOCK]        = { .len = sizeof(struct can_clock) },
>          [IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct
> can_berr_counter) },
>          [IFLA_CAN_DATA_BITTIMING]
>                                  = { .len = sizeof(struct can_bittiming)
> },
>          [IFLA_CAN_DATA_BITTIMING_CONST]
>                                  = { .len = sizeof(struct
> can_bittiming_const) },
> };
> 
> To add IFLA_CAN_TERMINATION we would just need
> 
> 	[IFLA_CAN_TERMINATION]   = { .type = NLA_U16 },
> 
> to enable a defined termination value from 1 to 65534 Ohms where 65535
> would be CAN_RESISTOR_DISABLED. In the CAN driver this given value is
> checked against the supported list of termination values and switched on
> if supported.
> To provide a list of possible termination values with
> IFLA_CAN_TERMINATION_CONST we need to pass an array of u16 constants to
> the userspace.
> 
> In opposite to our current netlink messages this array make contain 1..n
> constant values - so defining a fixed struct of e.g. 8 entries looks like
> a bad design.
> 
> But how to provide the array to the netlink interface correctly?
> 
> With the netlink type NLA_UNSPEC, NLA_NESTED, NLA_NESTED_COMPAT or
> NLA_BINARY??
> 
> See http://lxr.linux.no/#linux+v4.8.10/include/net/netlink.h#L160
> 
> The current
> 
> [IFLA_CAN_DATA_BITTIMING]  = { .len = sizeof(struct can_bittiming_const)
> },
> 
> seems to use NLA_UNSPEC which means the .len value is the _minimum_ length
> value.
> 
> Would this mean we can define
> 
> [IFLA_CAN_TERMINATION_CONST]   = { .len = sizeof(__u16) },
> 
> as a minimum length value and provide as much termination values as the
> CAN interface supports?

OK. This would be a simpler option.

Optionally, the can core could also check the given value against a list of valid termination values & round it to the nearest possible value. Something like below

possible_termination_values = { 60, 120, 240  } /* some random numbers */

- if user_val < 60 -> use 60
- if user_val > 240 -> use 240
- if user_val falls within a range of the possible values
    if ((range_low + (range_high - range_low) / 2)) <= user_val)
	user_val = range_low;
    else
      user_val = range_high;

   ret = driver_callback(user_val)

I felt the error reporting would be consistent. It is up to you.

Thanks,
Ramesh.

^ permalink raw reply

* Re: Troubleshooting bus errors, bus turns off during canplayer playback
From: Hans L @ 2016-12-21 18:08 UTC (permalink / raw)
  To: linux-can
In-Reply-To: <7f223f19-7e27-50f4-85c2-3c6785deae91@optusnet.com.au>

I'm reading some of these links talking about the issue of dealing
with multiple programs acessing the socket, but in the case I'm
dealing with, there's only one program connecting to the can socket on
this device.  That program is canplayer.  So I would need to patch
canplayer with that setsockopt code you are suggesting?

On Mon, Dec 19, 2016 at 11:01 PM, Tom Evans <tom_usenet@optusnet.com.au> wrote:
> On 20/12/16 15:53, Hans L wrote:
>>
>> I'm a developer writing firmware for a J1939 device and I have some
>> logs recorded from vehicles that I want to use as test data, playing
>> these on a "bench" setup.
>>
>> I am using a Beaglebone black running debian, with a PEAK PCAN-USB
>> adapter to play these logs on my test bus.
>>
>> When I attempt to play a log that has pretty high bus utilization, it
>> just quits after sending few frames with "No buffer space available".
>
>
> That's an old problem that everyone runs into eventually.
>
> It has come up before on this list.
>
> Here's something I posted in March 2015:
>
> ====
>
> Sockets can block or return ENOBUFS. Ethernet blocks before it returns
> ENOBUFS like you'd expect. With CAN it does the opposite "out of the box"
> and needs to be fixed.
>
> http://socket-can.996257.n3.nabble.com/Solving-ENOBUFS-returned-by-write-td2886.html
>
>     With Ethernet, the transmit queue length is 1000 (which would
>     return ENOBUF) but before that happens it hits SO_SNDBUF,
>     which may be 108544, which is the total Data plus SKB, and
>     with an SKB size of about 200 that means it blocks at about
>     500 before it ENOBUFs at 1000.
>
>     With CAN, it would block at 500, but it ENOBUFs at 10 first with
>     the default queue depth!
>
> I do the following to get a 256-deep queue that blocks before it overflows:
>
>     /bin/echo 256 > /sys/class/net/can0/tx_queue_len
>     /bin/echo 256 > /sys/class/net/can1/tx_queue_len
>
>     int sndbuf = (250 + 8) * 256;
>     socklen_t socklen = sizeof(sndbuf);
>     /* Minimum socket buffer to try and get it blocking */
>     rc = setsockopt(pSkt->skt, SOL_SOCKET, SO_SNDBUF,
>                     &sndbuf, sizeof(sndbuf));
>
> You might also like to read:
>
> http://rtime.felk.cvut.cz/can/socketcan-qdisc-final.pdf
>
>     SocketCAN and queueing disciplines:
>     Final Report
>     M. Sojka, R. Lisov y, P. P   sa
>     Czech Technical University in Prague
>     July 20, 2012
>     Version 1.2
>
> Tom
>

^ permalink raw reply

* Re: CAN Termination API - was Re: Adding new CAN driver
From: Kołłątaj, Remigiusz @ 2016-12-21 17:54 UTC (permalink / raw)
  To: Oliver Hartkopp
  Cc: Ramesh Shanmugasundaram, Kurt Van Dijck, Alexander Stein,
	Marc Kleine-Budde, Uwe Bonnes, linux-can@vger.kernel.org
In-Reply-To: <18d3932b-8c74-a470-661d-7f2ee1a9f2f5@hartkopp.net>

Hi,

What about keeping possibility to set termination value as u16 but
without defining supported values? This will give us opportunity to
define every value and it will be up to driver to verify it. I know
such approach will be less convenient for a user as he will not get
list of supported terminations. On the other hand these values can be
simply logged (i.e. netdev_info/warn/err).

This is pretty similar to setting up bitrate in USB devices. They are
usually predefined and there is no way to learn supported values with
ip command

Regards,
Remik

On 25 November 2016 at 15:16, Oliver Hartkopp <socketcan@hartkopp.net> wrote:
> Hi all,
>
> I would like to pick up the termination API again.
>
>>>> +
>>>> +    /* array of supported discrete resistor values (Ohm) */
>>>> +    __u16 term[8];
>>
>>
>> Do you think 8 resistor values are enough?
>>
>
> see below ...
>
>>>> +};
>>>> +
>>>> +/*
>>>> + * CAN termination
>>>> + */
>>>> +struct can_termination {
>>>> +    __u16 term;
>>>> +};
>>>> +
>>>> +#define CAN_RESISTOR_INVALID    0x0000U    /* indicates no value /
>>>> unsupported */
>>>> +#define CAN_RESISTOR_DISABLED    0xFFFFU    /* indicates disabled
>>>> termination */
>>>> +
>>>>   #define CAN_CTRLMODE_LOOPBACK        0x01    /* Loopback mode */
>>>>   #define CAN_CTRLMODE_LISTENONLY        0x02    /* Listen-only mode */
>>>>   #define CAN_CTRLMODE_3_SAMPLES        0x04    /* Triple sampling
>>>> mode */
>>>> @@ -127,6 +153,8 @@ enum {
>>>>       IFLA_CAN_BERR_COUNTER,
>>>>       IFLA_CAN_DATA_BITTIMING,
>>>>       IFLA_CAN_DATA_BITTIMING_CONST,
>>>> +    IFLA_CAN_TERMINATION,
>>>> +    IFLA_CAN_TERMINATION_CONST,
>>>>       __IFLA_CAN_MAX
>>>>   };
>>>>
>>>> With this interface the ip tool can get the capabilities of the
>>>> termination analogue to the BITTIMING_CONST values.
>>>> And it can set the termination with IFLA_CAN_TERMINATION.
>>>
>>>
>>> ACK.
>
>
> I tried to get into the netlink stuff and I wonder how to make this API
> really simple.
>
> By now the netlink interface in linux/drivers/net/can/dev.c looks like:
>
> /*
>  * CAN netlink interface
>  */
> static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
>         [IFLA_CAN_STATE]        = { .type = NLA_U32 },
>         [IFLA_CAN_CTRLMODE]     = { .len = sizeof(struct can_ctrlmode) },
>         [IFLA_CAN_RESTART_MS]   = { .type = NLA_U32 },
>         [IFLA_CAN_RESTART]      = { .type = NLA_U32 },
>         [IFLA_CAN_BITTIMING]    = { .len = sizeof(struct can_bittiming) },
>         [IFLA_CAN_BITTIMING_CONST]
>                                 = { .len = sizeof(struct
> can_bittiming_const) },
>         [IFLA_CAN_CLOCK]        = { .len = sizeof(struct can_clock) },
>         [IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter)
> },
>         [IFLA_CAN_DATA_BITTIMING]
>                                 = { .len = sizeof(struct can_bittiming) },
>         [IFLA_CAN_DATA_BITTIMING_CONST]
>                                 = { .len = sizeof(struct
> can_bittiming_const) },
> };
>
> To add IFLA_CAN_TERMINATION we would just need
>
>         [IFLA_CAN_TERMINATION]   = { .type = NLA_U16 },
>
> to enable a defined termination value from 1 to 65534 Ohms where 65535 would
> be CAN_RESISTOR_DISABLED. In the CAN driver this given value is checked
> against the supported list of termination values and switched on if
> supported.
>
> To provide a list of possible termination values with
> IFLA_CAN_TERMINATION_CONST we need to pass an array of u16 constants to the
> userspace.
>
> In opposite to our current netlink messages this array make contain 1..n
> constant values - so defining a fixed struct of e.g. 8 entries looks like a
> bad design.
>
> But how to provide the array to the netlink interface correctly?
>
> With the netlink type NLA_UNSPEC, NLA_NESTED, NLA_NESTED_COMPAT or
> NLA_BINARY??
>
> See http://lxr.linux.no/#linux+v4.8.10/include/net/netlink.h#L160
>
> The current
>
> [IFLA_CAN_DATA_BITTIMING]  = { .len = sizeof(struct can_bittiming_const) },
>
> seems to use NLA_UNSPEC which means the .len value is the _minimum_ length
> value.
>
> Would this mean we can define
>
> [IFLA_CAN_TERMINATION_CONST]   = { .len = sizeof(__u16) },
>
> as a minimum length value and provide as much termination values as the CAN
> interface supports?
>
> Regards,
> Oliver
>



-- 
________________________

Remigiusz Kołłątaj

Mobica Ltd

Software Consultant, CTO Team

Skype: remigiusz.kollataj

www.mobica.com


Mobica is a provider of software engineering, testing and consultancy
services based in the UK, Poland and the USA, with a worldwide
customer base. We have a proven track record in delivering innovative
solutions to some of the world’s best-known companies in a range of
sectors including Automotive, Mobile, Semiconductor, Finance, TV,
Marine and Aviation.

^ permalink raw reply

* Re: [PATCH] can: mcba_usb: Add support for Microchip CAN BUS Analyzer.
From: Kołłątaj, Remigiusz @ 2016-12-21 17:18 UTC (permalink / raw)
  To: Oliver Hartkopp; +Cc: linux-can
In-Reply-To: <31c2110b-f12c-b089-5595-645b16c8095f@hartkopp.net>

Hi Oliver,

On 20 December 2016 at 19:34, Oliver Hartkopp <socketcan@hartkopp.net> wrote:
> Hi Remik,
>
> On 12/20/2016 07:55 AM, Kołłątaj, Remigiusz wrote:
>>
>> Could someone tell me what happened with the patch below? Is there
>> something more I shall do with it?
>
>
> I'm generally fine with the patch when the debug stuff is removed

Do you mean staff "controlled" by MCBA_*_DEBUG macros? I am wondering
if there is a chance to refactor this part and keep it in code? It is
very useful to learn device state.

> agreed on a common netlink interface to configure the termination.

Can we keep sysfs setting for now? I will update the driver once
netlink API will be available. I will share my thoughts about the
interfaces in the email thread you have mentioned.

Regards,
Remik

>> On 4 November 2016 at 09:03, Remigiusz Kollataj
>> <remigiusz.kollataj@mobica.com> wrote:
>>>
>>>
>>> SocketCAN driver for Microchip CAN BUS Analyzer
>>> (http://www.microchip.com/development-tools/)
>>>
>>> Main features (v1):
>>> * Basic and extended frames support
>>> * RTR frames support
>>> * Supported hard-coded speeds: 20000, 33333, 50000, 80000, 83333, 100000,
>>>   125000, 150000, 175000, 200000, 225000, 250000, 275000, 300000, 500000,
>>>   625000, 800000, 1000000.
>>> * Configuration of built-in termination via sysfs
>>>
>>> Signed-off-by: Remigiusz Kollataj <remigiusz.kollataj@mobica.com>
>>> ---
>>>  drivers/net/can/usb/Kconfig    |    6 +
>>>  drivers/net/can/usb/Makefile   |    1 +
>>>  drivers/net/can/usb/mcba_usb.c | 1210
>>> ++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 1217 insertions(+)
>>>  create mode 100644 drivers/net/can/usb/mcba_usb.c
>>>
>>> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
>>> index 8483a40..2d0313e 100644
>>> --- a/drivers/net/can/usb/Kconfig
>>> +++ b/drivers/net/can/usb/Kconfig
>>> @@ -81,4 +81,10 @@ config CAN_8DEV_USB
>>>           This driver supports the USB2CAN interface
>>>           from 8 devices (http://www.8devices.com).
>>>
>>> +config CAN_MCBA_USB
>>> +       tristate "Microchip CAN BUS Analyzer interface"
>>> +       ---help---
>>> +         This driver supports the CAN BUS Analyzer interface
>>> +         from Microchip (http://www.microchip.com/development-tools/).
>>> +
>>>  endmenu
>>> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
>>> index a64cf98..164453f 100644
>>> --- a/drivers/net/can/usb/Makefile
>>> +++ b/drivers/net/can/usb/Makefile
>>> @@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
>>>  obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
>>>  obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
>>>  obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
>>> +obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o
>>> diff --git a/drivers/net/can/usb/mcba_usb.c
>>> b/drivers/net/can/usb/mcba_usb.c
>>> new file mode 100644
>>> index 0000000..a4fa8ba
>>> --- /dev/null
>>> +++ b/drivers/net/can/usb/mcba_usb.c
>>> @@ -0,0 +1,1210 @@
>>> +/* SocketCAN driver for Microchip CAN BUS Analyzer Tool
>>> + *
>>> + * Copyright (C) 2016 Mobica Limited
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> it
>>> + * under the terms of the GNU General Public License as published
>>> + * by the Free Software Foundation; version 2 of the License.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>> + * General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> along
>>> + * with this program.
>>> + *
>>> + * This driver is inspired by the 4.6.2 version of
>>> net/can/usb/usb_8dev.c
>>> + */
>>> +
>>> +#include <linux/usb.h>
>>> +#include <linux/can/dev.h>
>>> +
>>> +/* vendor and product id */
>>> +#define MCBA_MODULE_NAME         "mcba_usb"
>>> +#define MCBA_VENDOR_ID           0x04d8
>>> +#define MCBA_PRODUCT_ID          0x0a30
>>> +
>>> +/* driver constants */
>>> +#define MCBA_MAX_RX_URBS         20
>>> +#define MCBA_MAX_TX_URBS         20
>>> +#define MCBA_CTX_FREE            MCBA_MAX_TX_URBS
>>> +
>>> +/* RX buffer must be bigger than msg size since at the
>>> + * beggining USB messages are stacked.
>>> + */
>>> +#define MCBA_USB_RX_BUFF_SIZE    64
>>> +#define MCBA_USB_TX_BUFF_SIZE    (sizeof(struct mcba_usb_msg))
>>> +
>>> +/* MCBA endpoint numbers */
>>> +#define MCBA_USB_EP_IN           1
>>> +#define MCBA_USB_EP_OUT          1
>>> +
>>> +/* Not required by driver itself as CANBUS is USB based
>>> + * Used internally by candev for bitrate calculation
>>> + */
>>> +#define MCBA_CAN_CLOCK           40000000
>>> +
>>> +/* Microchip command id */
>>> +#define MBCA_CMD_RECEIVE_MESSAGE                0xE3
>>> +#define MBCA_CMD_I_AM_ALIVE_FROM_CAN            0xF5
>>> +#define MBCA_CMD_I_AM_ALIVE_FROM_USB            0xF7
>>> +#define MBCA_CMD_CHANGE_BIT_RATE                0xA1
>>> +#define MBCA_CMD_TRANSMIT_MESSAGE_EV            0xA3
>>> +#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE   0xA8
>>> +#define MBCA_CMD_READ_FW_VERSION                0xA9
>>> +#define MBCA_CMD_NOTHING_TO_SEND                0xFF
>>> +#define MBCA_CMD_TRANSMIT_MESSAGE_RSP           0xE2
>>> +
>>> +/* debug module parameter handling */
>>> +#define MCBA_PARAM_DEBUG_DISABLE    0
>>> +#define MCBA_PARAM_DEBUG_USB        1
>>> +#define MCBA_PARAM_DEBUG_CAN        2
>>> +#define MCBA_IS_USB_DEBUG()         (debug & MCBA_PARAM_DEBUG_USB)
>>> +#define MCBA_IS_CAN_DEBUG()         (debug & MCBA_PARAM_DEBUG_CAN)
>>> +
>>> +#define MCBA_VER_REQ_USB             1
>>> +#define MCBA_VER_REQ_CAN             2
>>> +
>>> +#define MCBA_CAN_S_SID0_SID2_MASK    0x7
>>> +#define MCBA_CAN_S_SID3_SID10_MASK   0x7F8
>>> +#define MCBA_CAN_S_SID3_SID10_SHIFT  3
>>> +
>>> +#define MCBA_CAN_EID0_EID7_MASK      0xff
>>> +#define MCBA_CAN_EID8_EID15_MASK     0xff00
>>> +#define MCBA_CAN_EID16_EID17_MASK    0x30000
>>> +#define MCBA_CAN_E_SID0_SID2_MASK    0x1c0000
>>> +#define MCBA_CAN_E_SID3_SID10_MASK   0x1fe00000
>>> +#define MCBA_CAN_EID8_EID15_SHIFT    8
>>> +#define MCBA_CAN_EID16_EID17_SHIFT   16
>>> +#define MCBA_CAN_E_SID0_SID2_SHIFT   18
>>> +#define MCBA_CAN_E_SID3_SID10_SHIFT  21
>>> +
>>> +#define MCBA_SIDL_SID0_SID2_MASK     0xe0
>>> +#define MCBA_SIDL_EXID_MASK          0x8
>>> +#define MCBA_SIDL_EID16_EID17_MASK   0x3
>>> +#define MCBA_SIDL_SID0_SID2_SHIFT    5
>>> +
>>> +#define MCBA_DLC_MASK                0xf
>>> +#define MCBA_DLC_RTR_MASK            0x40
>>> +
>>> +#define MCBA_CAN_RTR_MASK            0x40000000
>>> +#define MCBA_CAN_EXID_MASK           0x80000000
>>> +
>>> +#define MCBA_SET_S_SIDL(can_id)\
>>> +(((can_id) & MCBA_CAN_S_SID0_SID2_MASK) << MCBA_SIDL_SID0_SID2_SHIFT)
>>> +
>>> +#define MCBA_SET_E_SIDL(can_id)\
>>> +(((((can_id) & MCBA_CAN_E_SID0_SID2_MASK) >>
>>> MCBA_CAN_E_SID0_SID2_SHIFT)\
>>> +<< MCBA_SIDL_SID0_SID2_SHIFT) |\
>>> +(((can_id) & MCBA_CAN_EID16_EID17_MASK) >> MCBA_CAN_EID16_EID17_SHIFT)
>>> |\
>>> +MCBA_SIDL_EXID_MASK)
>>> +
>>> +#define MCBA_SET_S_SIDH(can_id)\
>>> +(((can_id) & MCBA_CAN_S_SID3_SID10_MASK) >> MCBA_CAN_S_SID3_SID10_SHIFT)
>>> +
>>> +#define MCBA_SET_E_SIDH(can_id)\
>>> +(((can_id) & MCBA_CAN_E_SID3_SID10_MASK) >> MCBA_CAN_E_SID3_SID10_SHIFT)
>>> +
>>> +#define MCBA_SET_EIDL(can_id)\
>>> +((can_id) & MCBA_CAN_EID0_EID7_MASK)
>>> +
>>> +#define MCBA_SET_EIDH(can_id)\
>>> +(((can_id) & MCBA_CAN_EID8_EID15_MASK) >> MCBA_CAN_EID8_EID15_SHIFT)
>>> +
>>> +#define MCBA_CAN_GET_SID(usb_msg)\
>>> +((((usb_msg)->sidl & MCBA_SIDL_SID0_SID2_MASK) >>
>>> MCBA_SIDL_SID0_SID2_SHIFT) |\
>>> +((usb_msg)->sidh << MCBA_CAN_S_SID3_SID10_SHIFT))
>>> +
>>> +#define MCBA_CAN_GET_EID(usb_msg)\
>>> +((((usb_msg)->sidh << MCBA_CAN_E_SID3_SID10_SHIFT) |\
>>> +(((usb_msg)->sidl & MCBA_SIDL_SID0_SID2_MASK) >>
>>> MCBA_SIDL_SID0_SID2_SHIFT) \
>>> +<< MCBA_CAN_E_SID0_SID2_SHIFT) |\
>>> +(((usb_msg)->sidl & MCBA_SIDL_EID16_EID17_MASK) \
>>> +<< MCBA_CAN_EID16_EID17_SHIFT) |\
>>> +((usb_msg)->eidh << MCBA_CAN_EID8_EID15_SHIFT) |\
>>> +(usb_msg)->eidl |\
>>> +MCBA_CAN_EXID_MASK)
>>> +
>>> +#define MCBA_RX_IS_EXID(usb_msg)    ((usb_msg)->sidl &
>>> MCBA_SIDL_EXID_MASK)
>>> +#define MCBA_RX_IS_RTR(usb_msg)     ((usb_msg)->dlc & MCBA_DLC_RTR_MASK)
>>> +#define MCBA_TX_IS_EXID(can_frame)  ((can_frame)->can_id &
>>> MCBA_CAN_EXID_MASK)
>>> +#define MCBA_TX_IS_RTR(can_frame)   ((can_frame)->can_id &
>>> MCBA_CAN_RTR_MASK)
>>> +
>>> +struct mcba_usb_ctx {
>>> +       struct mcba_priv *priv;
>>> +       u32 ndx;
>>> +       u8 dlc;
>>> +       bool can;
>>> +};
>>> +
>>> +/* Structure to hold all of our device specific stuff */
>>> +struct mcba_priv {
>>> +       struct can_priv can; /* must be the first member */
>>> +       struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
>>> +       struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
>>> +
>>> +       struct usb_device *udev;
>>> +       struct net_device *netdev;
>>> +       struct usb_anchor tx_submitted;
>>> +       struct usb_anchor rx_submitted;
>>> +       struct can_berr_counter bec;
>>> +       u8 termination_state;
>>> +       bool usb_ka_first_pass;
>>> +       bool can_ka_first_pass;
>>> +};
>>> +
>>> +/* command frame */
>>> +struct __packed mcba_usb_msg_can {
>>> +       u8 cmd_id;
>>> +       u8 eidh;
>>> +       u8 eidl;
>>> +       u8 sidh;
>>> +       u8 sidl;
>>> +       u8 dlc;
>>> +       u8 data[8];
>>> +       u8 timestamp[4];
>>> +       u8 checksum;
>>> +};
>>> +
>>> +/* command frame */
>>> +struct __packed mcba_usb_msg {
>>> +       u8 cmd_id;
>>> +       u8 unused[18];
>>> +};
>>> +
>>> +struct __packed mcba_usb_msg_ka_usb {
>>> +       u8 cmd_id;
>>> +       u8 termination_state;
>>> +       u8 soft_ver_major;
>>> +       u8 soft_ver_minor;
>>> +       u8 unused[15];
>>> +};
>>> +
>>> +struct __packed mcba_usb_msg_ka_can {
>>> +       u8 cmd_id;
>>> +       u8 tx_err_cnt;
>>> +       u8 rx_err_cnt;
>>> +       u8 rx_buff_ovfl;
>>> +       u8 tx_bus_off;
>>> +       u8 can_bitrate_hi;
>>> +       u8 can_bitrate_lo;
>>> +       u8 rx_lost_lo;
>>> +       u8 rx_lost_hi;
>>> +       u8 can_stat;
>>> +       u8 soft_ver_major;
>>> +       u8 soft_ver_minor;
>>> +       u8 debug_mode;
>>> +       u8 test_complete;
>>> +       u8 test_result;
>>> +       u8 unused[4];
>>> +};
>>> +
>>> +struct __packed mcba_usb_msg_change_bitrate {
>>> +       u8 cmd_id;
>>> +       u8 bitrate_hi;
>>> +       u8 bitrate_lo;
>>> +       u8 unused[16];
>>> +};
>>> +
>>> +struct __packed mcba_usb_msg_terminaton {
>>> +       u8 cmd_id;
>>> +       u8 termination;
>>> +       u8 unused[17];
>>> +};
>>> +
>>> +struct __packed mcba_usb_msg_fw_ver {
>>> +       u8 cmd_id;
>>> +       u8 pic;
>>> +       u8 unused[17];
>>> +};
>>> +
>>> +struct bitrate_settings {
>>> +       struct can_bittiming bt;
>>> +       u16 kbps;
>>> +};
>>> +
>>> +/* Required by can-dev but not for the sake of driver as CANBUS is USB
>>> based */
>>> +static const struct can_bittiming_const mcba_bittiming_const = {
>>> +       .name = "mcba_usb",
>>> +       .tseg1_min = 1,
>>> +       .tseg1_max = 8,
>>> +       .tseg2_min = 1,
>>> +       .tseg2_max = 8,
>>> +       .sjw_max = 4,
>>> +       .brp_min = 2,
>>> +       .brp_max = 128,
>>> +       .brp_inc = 2,
>>> +};
>>> +
>>> +/* predefined values hardcoded in device's firmware */
>>> +static const struct bitrate_settings br_settings[] = {
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 19940,
>>> +                       .sample_point = 700,
>>> +                       .tq = 2500,
>>> +                       .prop_seg = 5,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 6,
>>> +                       .sjw = 1,
>>> +                       .brp = 100,
>>> +               },
>>> +               .kbps = 20
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 33333,
>>> +                       .sample_point = 680,
>>> +                       .tq = 1200,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 8,
>>> +                       .sjw = 1,
>>> +                       .brp = 48,
>>> +               },
>>> +               .kbps = 33
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 50000,
>>> +                       .sample_point = 800,
>>> +                       .tq = 1000,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 7,
>>> +                       .phase_seg2 = 4,
>>> +                       .sjw = 1,
>>> +                       .brp = 40,
>>> +               },
>>> +               .kbps = 50
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 80000,
>>> +                       .sample_point = 680,
>>> +                       .tq = 500,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 8,
>>> +                       .sjw = 1,
>>> +                       .brp = 20,
>>> +               },
>>> +               .kbps = 80
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 83333,
>>> +                       .sample_point = 708,
>>> +                       .tq = 500,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 7,
>>> +                       .sjw = 1,
>>> +                       .brp = 20,
>>> +               },
>>> +               .kbps = 83
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 100000,
>>> +                       .sample_point = 700,
>>> +                       .tq = 1000,
>>> +                       .prop_seg = 1,
>>> +                       .phase_seg1 = 5,
>>> +                       .phase_seg2 = 3,
>>> +                       .sjw = 1,
>>> +                       .brp = 40,
>>> +               },
>>> +               .kbps = 100
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 125000,
>>> +                       .sample_point = 600,
>>> +                       .tq = 400,
>>> +                       .prop_seg = 3,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 8,
>>> +                       .sjw = 1,
>>> +                       .brp = 16,
>>> +               },
>>> +               .kbps = 125
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 150375,
>>> +                       .sample_point = 789,
>>> +                       .tq = 350,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 6,
>>> +                       .phase_seg2 = 4,
>>> +                       .sjw = 1,
>>> +                       .brp = 14,
>>> +               },
>>> +               .kbps = 150
>>> +       },
>>> +
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 175438,
>>> +                       .sample_point = 789,
>>> +                       .tq = 300,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 6,
>>> +                       .phase_seg2 = 4,
>>> +                       .sjw = 1,
>>> +                       .brp = 12,
>>> +               },
>>> +               .kbps = 175
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 200000,
>>> +                       .sample_point = 680,
>>> +                       .tq = 200,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 8,
>>> +                       .sjw = 1,
>>> +                       .brp = 8,
>>> +               },
>>> +               .kbps = 200
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 227272,
>>> +                       .sample_point = 772,
>>> +                       .tq = 200,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 5,
>>> +                       .sjw = 1,
>>> +                       .brp = 8,
>>> +               },
>>> +               .kbps = 225
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 250000,
>>> +                       .sample_point = 600,
>>> +                       .tq = 200,
>>> +                       .prop_seg = 3,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 8,
>>> +                       .sjw = 1,
>>> +                       .brp = 8,
>>> +               },
>>> +               .kbps = 250
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 277777,
>>> +                       .sample_point = 708,
>>> +                       .tq = 150,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 7,
>>> +                       .sjw = 1,
>>> +                       .brp = 6,
>>> +               },
>>> +               .kbps = 275
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 303030,
>>> +                       .sample_point = 772,
>>> +                       .tq = 150,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 5,
>>> +                       .sjw = 1,
>>> +                       .brp = 6,
>>> +               },
>>> +               .kbps = 300
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 500000,
>>> +                       .sample_point = 600,
>>> +                       .tq = 100,
>>> +                       .prop_seg = 3,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 8,
>>> +                       .sjw = 1,
>>> +                       .brp = 4,
>>> +               },
>>> +               .kbps = 500
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 625000,
>>> +                       .sample_point = 750,
>>> +                       .tq = 200,
>>> +                       .prop_seg = 1,
>>> +                       .phase_seg1 = 4,
>>> +                       .phase_seg2 = 2,
>>> +                       .sjw = 1,
>>> +                       .brp = 8,
>>> +               },
>>> +               .kbps = 625
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 800000,
>>> +                       .sample_point = 680,
>>> +                       .tq = 50,
>>> +                       .prop_seg = 8,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 8,
>>> +                       .sjw = 1,
>>> +                       .brp = 2,
>>> +               },
>>> +               .kbps = 800
>>> +       },
>>> +       {
>>> +               .bt = {
>>> +                       .bitrate = 1000000,
>>> +                       .sample_point = 600,
>>> +                       .tq = 50,
>>> +                       .prop_seg = 3,
>>> +                       .phase_seg1 = 8,
>>> +                       .phase_seg2 = 8,
>>> +                       .sjw = 1,
>>> +                       .brp = 2,
>>> +               },
>>> +               .kbps = 1000
>>> +       }
>>> +};
>>> +
>>> +static int debug;
>>> +module_param(debug, int, 0664);
>>> +MODULE_PARM_DESC(debug,
>>> +                "Binary flag to control device debug (keep alive) prints
>>> in dmesg. 0='Debug prints disabled' "
>>> +                __stringify(MCBA_PARAM_DEBUG_USB) "='PIC_USB debugs
>>> enabled' "
>>> +                __stringify(MCBA_PARAM_DEBUG_CAN) "='PIC_CAN debugs
>>> enabled'");
>>> +
>>> +static const struct usb_device_id mcba_usb_table[] = {
>>> +       { USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
>>> +       { } /* Terminating entry */
>>> +};
>>> +
>>> +MODULE_DEVICE_TABLE(usb, mcba_usb_table);
>>> +
>>> +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv,
>>> +                                struct mcba_usb_msg *usb_msg,
>>> +                                struct sk_buff *skb);
>>> +static void mcba_usb_xmit_cmd(struct mcba_priv *priv,
>>> +                             struct mcba_usb_msg *usb_msg);
>>> +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic);
>>> +static void mcba_usb_xmit_termination(struct mcba_priv *priv, u8
>>> termination);
>>> +static inline void mcba_init_ctx(struct mcba_priv *priv);
>>> +
>>> +static ssize_t termination_show(struct device *dev,
>>> +                               struct device_attribute *attr, char *buf)
>>> +{
>>> +       struct net_device *netdev = to_net_dev(dev);
>>> +       struct mcba_priv *priv = netdev_priv(netdev);
>>> +
>>> +       return sprintf(buf, "%hhu\n", priv->termination_state);
>>> +}
>>> +
>>> +static ssize_t termination_store(struct device *dev,
>>> +                                struct device_attribute *attr,
>>> +                                const char *buf, size_t count)
>>> +{
>>> +       struct net_device *netdev = to_net_dev(dev);
>>> +       struct mcba_priv *priv = netdev_priv(netdev);
>>> +       int tmp = -1;
>>> +       int ret = -1;
>>> +
>>> +       ret = kstrtoint(buf, 10, &tmp);
>>> +
>>> +       if ((ret == 0) && ((tmp == 0) || (tmp == 1))) {
>>> +               priv->termination_state = tmp;
>>> +               mcba_usb_xmit_termination(priv, priv->termination_state);
>>> +       }
>>> +
>>> +       return count;
>>> +}
>>> +
>>> +static struct device_attribute termination_attr = {
>>> +       .attr = {
>>> +               .name = "termination",
>>> +               .mode = 0666 },
>>> +       .show   = termination_show,
>>> +       .store  = termination_store
>>> +};
>>> +
>>> +static void mcba_usb_process_can(struct mcba_priv *priv,
>>> +                                struct mcba_usb_msg_can *msg)
>>> +{
>>> +       struct can_frame *cf;
>>> +       struct sk_buff *skb;
>>> +       struct net_device_stats *stats = &priv->netdev->stats;
>>> +
>>> +       skb = alloc_can_skb(priv->netdev, &cf);
>>> +       if (!skb)
>>> +               return;
>>> +
>>> +       if (MCBA_RX_IS_EXID(msg))
>>> +               cf->can_id = MCBA_CAN_GET_EID(msg);
>>> +       else
>>> +               cf->can_id = MCBA_CAN_GET_SID(msg);
>>> +
>>> +       if (MCBA_RX_IS_RTR(msg))
>>> +               cf->can_id |= MCBA_CAN_RTR_MASK;
>>> +
>>> +       cf->can_dlc = msg->dlc & MCBA_DLC_MASK;
>>> +
>>> +       memcpy(cf->data, msg->data, cf->can_dlc);
>>> +
>>> +       stats->rx_packets++;
>>> +       stats->rx_bytes += cf->can_dlc;
>>> +       netif_rx(skb);
>>> +}
>>> +
>>> +static void mcba_usb_process_ka_usb(struct mcba_priv *priv,
>>> +                                   struct mcba_usb_msg_ka_usb *msg)
>>> +{
>>> +       if (unlikely(MCBA_IS_USB_DEBUG())) {
>>> +               netdev_info(priv->netdev,
>>> +                           "USB_KA: termination %hhu, ver_maj %hhu,
>>> soft_min %hhu\n",
>>> +                           msg->termination_state, msg->soft_ver_major,
>>> +                           msg->soft_ver_minor);
>>> +       }
>>> +
>>> +       if (unlikely(priv->usb_ka_first_pass)) {
>>> +               netdev_info(priv->netdev,
>>> +                           "PIC USB version %hhu.%hhu\n",
>>> +                           msg->soft_ver_major, msg->soft_ver_minor);
>>> +
>>> +               priv->usb_ka_first_pass = false;
>>> +       }
>>> +
>>> +       priv->termination_state = msg->termination_state;
>>> +}
>>> +
>>> +static void mcba_usb_process_ka_can(struct mcba_priv *priv,
>>> +                                   struct mcba_usb_msg_ka_can *msg)
>>> +{
>>> +       if (unlikely(MCBA_IS_CAN_DEBUG())) {
>>> +               netdev_info(priv->netdev,
>>> +                           "CAN_KA: tx_err_cnt %hhu, rx_err_cnt %hhu,
>>> rx_buff_ovfl %hhu, tx_bus_off %hhu, can_bitrate %hu, rx_lost %hu, can_stat
>>> %hhu, soft_ver %hhu.%hhu, debug_mode %hhu, test_complete %hhu, test_result
>>> %hhu\n",
>>> +                           msg->tx_err_cnt, msg->rx_err_cnt,
>>> msg->rx_buff_ovfl,
>>> +                           msg->tx_bus_off,
>>> +                           ((msg->can_bitrate_hi << 8) +
>>> msg->can_bitrate_lo),
>>> +                           ((msg->rx_lost_hi >> 8) + msg->rx_lost_lo),
>>> +                           msg->can_stat, msg->soft_ver_major,
>>> +                           msg->soft_ver_minor,
>>> +                           msg->debug_mode, msg->test_complete,
>>> +                           msg->test_result);
>>> +       }
>>> +
>>> +       if (unlikely(priv->can_ka_first_pass)) {
>>> +               netdev_info(priv->netdev,
>>> +                           "PIC CAN version %hhu.%hhu\n",
>>> +                           msg->soft_ver_major, msg->soft_ver_minor);
>>> +
>>> +               priv->can_ka_first_pass = false;
>>> +       }
>>> +
>>> +       priv->bec.txerr = msg->tx_err_cnt;
>>> +       priv->bec.rxerr = msg->rx_err_cnt;
>>> +}
>>> +
>>> +static void mcba_usb_process_rx(struct mcba_priv *priv,
>>> +                               struct mcba_usb_msg *msg)
>>> +{
>>> +       switch (msg->cmd_id) {
>>> +       case MBCA_CMD_I_AM_ALIVE_FROM_CAN:
>>> +               mcba_usb_process_ka_can(priv,
>>> +                                       (struct mcba_usb_msg_ka_can
>>> *)msg);
>>> +               break;
>>> +
>>> +       case MBCA_CMD_I_AM_ALIVE_FROM_USB:
>>> +               mcba_usb_process_ka_usb(priv,
>>> +                                       (struct mcba_usb_msg_ka_usb
>>> *)msg);
>>> +               break;
>>> +
>>> +       case MBCA_CMD_RECEIVE_MESSAGE:
>>> +               mcba_usb_process_can(priv, (struct mcba_usb_msg_can
>>> *)msg);
>>> +               break;
>>> +
>>> +       case MBCA_CMD_NOTHING_TO_SEND:
>>> +               /* Side effect of communication between PIC_USB and
>>> PIC_CAN.
>>> +                * PIC_CAN is telling us that it has nothing to send
>>> +                */
>>> +               break;
>>> +
>>> +       case MBCA_CMD_TRANSMIT_MESSAGE_RSP:
>>> +               /* Transmission response from the device containing
>>> timestamp */
>>> +               break;
>>> +
>>> +       default:
>>> +               netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)",
>>> +                           msg->cmd_id);
>>> +               break;
>>> +       }
>>> +}
>>> +
>>> +/* Callback for reading data from device
>>> + *
>>> + * Check urb status, call read function and resubmit urb read operation.
>>> + */
>>> +static void mcba_usb_read_bulk_callback(struct urb *urb)
>>> +{
>>> +       struct mcba_priv *priv = urb->context;
>>> +       struct net_device *netdev;
>>> +       int retval;
>>> +       int pos = 0;
>>> +
>>> +       netdev = priv->netdev;
>>> +
>>> +       if (!netif_device_present(netdev))
>>> +               return;
>>> +
>>> +       switch (urb->status) {
>>> +       case 0: /* success */
>>> +               break;
>>> +
>>> +       case -ENOENT:
>>> +       case -ESHUTDOWN:
>>> +               return;
>>> +
>>> +       default:
>>> +               netdev_info(netdev, "Rx URB aborted (%d)\n",
>>> +                           urb->status);
>>> +
>>> +               goto resubmit_urb;
>>> +       }
>>> +
>>> +       while (pos < urb->actual_length) {
>>> +               struct mcba_usb_msg *msg;
>>> +
>>> +               if (pos + sizeof(struct mcba_usb_msg) >
>>> urb->actual_length) {
>>> +                       netdev_err(priv->netdev, "format error\n");
>>> +                       break;
>>> +               }
>>> +
>>> +               msg = (struct mcba_usb_msg *)(urb->transfer_buffer +
>>> pos);
>>> +               mcba_usb_process_rx(priv, msg);
>>> +
>>> +               pos += sizeof(struct mcba_usb_msg);
>>> +       }
>>> +
>>> +resubmit_urb:
>>> +
>>> +       usb_fill_bulk_urb(urb, priv->udev,
>>> +                         usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_OUT),
>>> +                         urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE,
>>> +                         mcba_usb_read_bulk_callback, priv);
>>> +
>>> +       retval = usb_submit_urb(urb, GFP_ATOMIC);
>>> +
>>> +       if (retval == -ENODEV)
>>> +               netif_device_detach(netdev);
>>> +       else if (retval)
>>> +               netdev_err(netdev, "failed resubmitting read bulk urb:
>>> %d\n",
>>> +                          retval);
>>> +}
>>> +
>>> +/* Start USB device */
>>> +static int mcba_usb_start(struct mcba_priv *priv)
>>> +{
>>> +       struct net_device *netdev = priv->netdev;
>>> +       int err, i;
>>> +
>>> +       for (i = 0; i < MCBA_MAX_RX_URBS; i++) {
>>> +               struct urb *urb = NULL;
>>> +               u8 *buf;
>>> +
>>> +               /* create a URB, and a buffer for it */
>>> +               urb = usb_alloc_urb(0, GFP_KERNEL);
>>> +               if (!urb) {
>>> +                       netdev_err(netdev, "No memory left for URBs\n");
>>> +                       err = -ENOMEM;
>>> +                       break;
>>> +               }
>>> +
>>> +               buf = usb_alloc_coherent(priv->udev,
>>> MCBA_USB_RX_BUFF_SIZE,
>>> +                                        GFP_KERNEL,
>>> +                                        &urb->transfer_dma);
>>> +               if (!buf) {
>>> +                       netdev_err(netdev, "No memory left for USB
>>> buffer\n");
>>> +                       usb_free_urb(urb);
>>> +                       err = -ENOMEM;
>>> +                       break;
>>> +               }
>>> +
>>> +               usb_fill_bulk_urb(urb, priv->udev,
>>> +                                 usb_rcvbulkpipe(priv->udev,
>>> +                                                 MCBA_USB_EP_IN),
>>> +                                 buf, MCBA_USB_RX_BUFF_SIZE,
>>> +                                 mcba_usb_read_bulk_callback, priv);
>>> +               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
>>> +               usb_anchor_urb(urb, &priv->rx_submitted);
>>> +
>>> +               err = usb_submit_urb(urb, GFP_KERNEL);
>>> +               if (err) {
>>> +                       usb_unanchor_urb(urb);
>>> +                       usb_free_coherent(priv->udev,
>>> MCBA_USB_RX_BUFF_SIZE,
>>> +                                         buf, urb->transfer_dma);
>>> +                       usb_free_urb(urb);
>>> +                       break;
>>> +               }
>>> +
>>> +               /* Drop reference, USB core will take care of freeing it
>>> */
>>> +               usb_free_urb(urb);
>>> +       }
>>> +
>>> +       /* Did we submit any URBs */
>>> +       if (i == 0) {
>>> +               netdev_warn(netdev, "couldn't setup read URBs\n");
>>> +               return err;
>>> +       }
>>> +
>>> +       /* Warn if we've couldn't transmit all the URBs */
>>> +       if (i < MCBA_MAX_RX_URBS)
>>> +               netdev_warn(netdev, "rx performance may be slow\n");
>>> +
>>> +       priv->can.state = CAN_STATE_ERROR_ACTIVE;
>>> +
>>> +       mcba_init_ctx(priv);
>>> +       mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB);
>>> +       mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN);
>>> +
>>> +       return err;
>>> +}
>>> +
>>> +static inline void mcba_init_ctx(struct mcba_priv *priv)
>>> +{
>>> +       int i = 0;
>>> +
>>> +       for (i = 0; i < MCBA_MAX_TX_URBS; i++)
>>> +               priv->tx_context[i].ndx = MCBA_CTX_FREE;
>>> +}
>>> +
>>> +static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct
>>> mcba_priv *priv)
>>> +{
>>> +       int i = 0;
>>> +       struct mcba_usb_ctx *ctx = 0;
>>> +
>>> +       for (i = 0; i < MCBA_MAX_TX_URBS; i++) {
>>> +               if (priv->tx_context[i].ndx == MCBA_CTX_FREE) {
>>> +                       ctx = &priv->tx_context[i];
>>> +                       ctx->ndx = i;
>>> +                       ctx->priv = priv;
>>> +                       break;
>>> +               }
>>> +       }
>>> +
>>> +       return ctx;
>>> +}
>>> +
>>> +static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx)
>>> +{
>>> +       ctx->ndx = MCBA_CTX_FREE;
>>> +       ctx->priv = 0;
>>> +       ctx->dlc = 0;
>>> +       ctx->can = false;
>>> +}
>>> +
>>> +static void mcba_usb_write_bulk_callback(struct urb *urb)
>>> +{
>>> +       struct mcba_usb_ctx *ctx = urb->context;
>>> +       struct net_device *netdev;
>>> +
>>> +       WARN_ON(!ctx);
>>> +
>>> +       netdev = ctx->priv->netdev;
>>> +
>>> +       if (ctx->can) {
>>> +               if (!netif_device_present(netdev))
>>> +                       return;
>>> +
>>> +               netdev->stats.tx_packets++;
>>> +               netdev->stats.tx_bytes += ctx->dlc;
>>> +
>>> +               can_get_echo_skb(netdev, ctx->ndx);
>>> +
>>> +               netif_wake_queue(netdev);
>>> +       }
>>> +
>>> +       /* free up our allocated buffer */
>>> +       usb_free_coherent(urb->dev, urb->transfer_buffer_length,
>>> +                         urb->transfer_buffer, urb->transfer_dma);
>>> +
>>> +       if (urb->status)
>>> +               netdev_info(netdev, "Tx URB aborted (%d)\n",
>>> +                           urb->status);
>>> +
>>> +       /* Release context */
>>> +       mcba_usb_free_ctx(ctx);
>>> +}
>>> +
>>> +/* Send data to device */
>>> +static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
>>> +                                      struct net_device *netdev)
>>> +{
>>> +       struct mcba_priv *priv = netdev_priv(netdev);
>>> +       struct can_frame *cf = (struct can_frame *)skb->data;
>>> +       struct mcba_usb_msg_can usb_msg;
>>> +
>>> +       usb_msg.cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV;
>>> +       memcpy(usb_msg.data, cf->data, sizeof(usb_msg.data));
>>> +
>>> +       if (MCBA_TX_IS_EXID(cf)) {
>>> +               usb_msg.sidl = MCBA_SET_E_SIDL(cf->can_id);
>>> +               usb_msg.sidh = MCBA_SET_E_SIDH(cf->can_id);
>>> +               usb_msg.eidl = MCBA_SET_EIDL(cf->can_id);
>>> +               usb_msg.eidh = MCBA_SET_EIDH(cf->can_id);
>>> +       } else {
>>> +               usb_msg.sidl = MCBA_SET_S_SIDL(cf->can_id);
>>> +               usb_msg.sidh = MCBA_SET_S_SIDH(cf->can_id);
>>> +               usb_msg.eidl = 0;
>>> +               usb_msg.eidh = 0;
>>> +       }
>>> +
>>> +       usb_msg.dlc = cf->can_dlc;
>>> +
>>> +       if (MCBA_TX_IS_RTR(cf))
>>> +               usb_msg.dlc |= MCBA_DLC_RTR_MASK;
>>> +
>>> +       return mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, skb);
>>> +}
>>> +
>>> +/* Send data to device */
>>> +static void mcba_usb_xmit_cmd(struct mcba_priv *priv,
>>> +                             struct mcba_usb_msg *usb_msg)
>>> +{
>>> +       mcba_usb_xmit(priv, usb_msg, 0);
>>> +}
>>> +
>>> +/* Send data to device */
>>> +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv,
>>> +                                struct mcba_usb_msg *usb_msg,
>>> +                                struct sk_buff *skb)
>>> +{
>>> +       struct net_device_stats *stats = &priv->netdev->stats;
>>> +       struct mcba_usb_ctx *ctx = 0;
>>> +       struct urb *urb;
>>> +       u8 *buf;
>>> +       int err;
>>> +
>>> +       ctx = mcba_usb_get_free_ctx(priv);
>>> +       if (!ctx) {
>>> +               /* Slow down tx path */
>>> +               netif_stop_queue(priv->netdev);
>>> +
>>> +               return NETDEV_TX_BUSY;
>>> +       }
>>> +
>>> +       if (skb) {
>>> +               ctx->dlc = ((struct mcba_usb_msg_can *)usb_msg)->dlc
>>> +                               & MCBA_DLC_MASK;
>>> +               can_put_echo_skb(skb, priv->netdev, ctx->ndx);
>>> +               ctx->can = true;
>>> +       } else {
>>> +               ctx->can = false;
>>> +       }
>>> +
>>> +       /* create a URB, and a buffer for it, and copy the data to the
>>> URB */
>>> +       urb = usb_alloc_urb(0, GFP_ATOMIC);
>>> +       if (!urb) {
>>> +               netdev_err(priv->netdev, "No memory left for URBs\n");
>>> +               goto nomem;
>>> +       }
>>> +
>>> +       buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE,
>>> GFP_ATOMIC,
>>> +                                &urb->transfer_dma);
>>> +       if (!buf) {
>>> +               netdev_err(priv->netdev, "No memory left for USB
>>> buffer\n");
>>> +               goto nomembuf;
>>> +       }
>>> +
>>> +       memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE);
>>> +
>>> +       usb_fill_bulk_urb(urb, priv->udev,
>>> +                         usb_sndbulkpipe(priv->udev, MCBA_USB_EP_OUT),
>>> buf,
>>> +                         MCBA_USB_TX_BUFF_SIZE,
>>> mcba_usb_write_bulk_callback,
>>> +                         ctx);
>>> +
>>> +       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
>>> +       usb_anchor_urb(urb, &priv->tx_submitted);
>>> +
>>> +       err = usb_submit_urb(urb, GFP_ATOMIC);
>>> +       if (unlikely(err))
>>> +               goto failed;
>>> +
>>> +       /* Release our reference to this URB, the USB core will
>>> eventually free
>>> +        * it entirely.
>>> +        */
>>> +       usb_free_urb(urb);
>>> +
>>> +       return NETDEV_TX_OK;
>>> +
>>> +failed:
>>> +       usb_unanchor_urb(urb);
>>> +       usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf,
>>> +                         urb->transfer_dma);
>>> +
>>> +       if (err == -ENODEV)
>>> +               netif_device_detach(priv->netdev);
>>> +       else
>>> +               netdev_warn(priv->netdev, "failed tx_urb %d\n", err);
>>> +
>>> +nomembuf:
>>> +       usb_free_urb(urb);
>>> +
>>> +nomem:
>>> +       can_free_echo_skb(priv->netdev, ctx->ndx);
>>> +       dev_kfree_skb(skb);
>>> +       stats->tx_dropped++;
>>> +
>>> +       return NETDEV_TX_OK;
>>> +}
>>> +
>>> +static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16
>>> bitrate)
>>> +{
>>> +       struct mcba_usb_msg_change_bitrate usb_msg;
>>> +
>>> +       usb_msg.cmd_id =  MBCA_CMD_CHANGE_BIT_RATE;
>>> +       usb_msg.bitrate_hi = (0xff00 & bitrate) >> 8;
>>> +       usb_msg.bitrate_lo = (0xff & bitrate);
>>> +
>>> +       mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
>>> +}
>>> +
>>> +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic)
>>> +{
>>> +       struct mcba_usb_msg_fw_ver usb_msg;
>>> +
>>> +       usb_msg.cmd_id = MBCA_CMD_READ_FW_VERSION;
>>> +       usb_msg.pic = pic;
>>> +
>>> +       mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
>>> +}
>>> +
>>> +static void mcba_usb_xmit_termination(struct mcba_priv *priv, u8
>>> termination)
>>> +{
>>> +       struct mcba_usb_msg_terminaton usb_msg;
>>> +
>>> +       usb_msg.cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE;
>>> +       usb_msg.termination = termination;
>>> +
>>> +       mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
>>> +}
>>> +
>>> +/* Open USB device */
>>> +static int mcba_usb_open(struct net_device *netdev)
>>> +{
>>> +       int err;
>>> +
>>> +       /* common open */
>>> +       err = open_candev(netdev);
>>> +       if (err)
>>> +               return err;
>>> +
>>> +       can_led_event(netdev, CAN_LED_EVENT_OPEN);
>>> +
>>> +       netif_start_queue(netdev);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static void mcba_urb_unlink(struct mcba_priv *priv)
>>> +{
>>> +       usb_kill_anchored_urbs(&priv->rx_submitted);
>>> +       usb_kill_anchored_urbs(&priv->tx_submitted);
>>> +}
>>> +
>>> +/* Close USB device */
>>> +static int mcba_usb_close(struct net_device *netdev)
>>> +{
>>> +       struct mcba_priv *priv = netdev_priv(netdev);
>>> +
>>> +       priv->can.state = CAN_STATE_STOPPED;
>>> +
>>> +       netif_stop_queue(netdev);
>>> +
>>> +       /* Stop polling */
>>> +       mcba_urb_unlink(priv);
>>> +
>>> +       close_candev(netdev);
>>> +
>>> +       can_led_event(netdev, CAN_LED_EVENT_STOP);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +/* Set network device mode
>>> + *
>>> + * Maybe we should leave this function empty, because the device
>>> + * set mode variable with open command.
>>> + */
>>> +static int mcba_net_set_mode(struct net_device *netdev, enum can_mode
>>> mode)
>>> +{
>>> +       return 0;
>>> +}
>>> +
>>> +static int mcba_net_get_berr_counter(const struct net_device *netdev,
>>> +                                    struct can_berr_counter *bec)
>>> +{
>>> +       struct mcba_priv *priv = netdev_priv(netdev);
>>> +
>>> +       bec->txerr = priv->bec.txerr;
>>> +       bec->rxerr = priv->bec.rxerr;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static const struct net_device_ops mcba_netdev_ops = {
>>> +       .ndo_open = mcba_usb_open,
>>> +       .ndo_stop = mcba_usb_close,
>>> +       .ndo_start_xmit = mcba_usb_start_xmit
>>> +};
>>> +
>>> +/* Microchip CANBUS has hardcoded bittiming values by default.
>>> + * This function sends request via USB to change the speed and align
>>> bittiming
>>> + * values for presentation purposes only
>>> + */
>>> +static int mcba_net_set_bittiming(struct net_device *netdev)
>>> +{
>>> +       u8 i;
>>> +       struct mcba_priv *priv = netdev_priv(netdev);
>>> +       struct can_bittiming *bt = &priv->can.bittiming;
>>> +       const struct bitrate_settings *settings = 0;
>>> +       const u8 setting_cnt = sizeof(br_settings) /
>>> +                              sizeof(struct bitrate_settings);
>>> +
>>> +       for (i = 0; i < setting_cnt; ++i)
>>> +               if (br_settings[i].bt.bitrate == bt->bitrate)
>>> +                       settings = &br_settings[i];
>>> +
>>> +       if (settings) {
>>> +               memcpy(bt, &settings->bt, sizeof(struct can_bittiming));
>>> +
>>> +               /* recalculate bitrate as it may be different than
>>> default */
>>> +               bt->bitrate = 1000000000 / ((bt->sjw + bt->prop_seg +
>>> +                                           bt->phase_seg1 +
>>> bt->phase_seg2) *
>>> +                                           bt->tq);
>>> +
>>> +               mcba_usb_xmit_change_bitrate(priv, settings->kbps);
>>> +       } else {
>>> +               netdev_err(netdev, "Unsupported bittrate (%u). Use one
>>> of: 20000, 33333, 50000, 80000, 83333, 100000, 125000, 150000, 175000,
>>> 200000, 225000, 250000, 275000, 300000, 500000, 625000, 800000, 1000000\n",
>>> +                          bt->bitrate);
>>> +
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int mcba_usb_probe(struct usb_interface *intf,
>>> +                         const struct usb_device_id *id)
>>> +{
>>> +       struct net_device *netdev;
>>> +       struct mcba_priv *priv;
>>> +       int err = -ENOMEM;
>>> +       struct usb_device *usbdev = interface_to_usbdev(intf);
>>> +
>>> +       dev_info(&intf->dev, "Microchip CAN BUS analizer connected\n");
>>> +
>>> +       netdev = alloc_candev(sizeof(struct mcba_priv),
>>> MCBA_MAX_TX_URBS);
>>> +       if (!netdev) {
>>> +               dev_err(&intf->dev, "Couldn't alloc candev\n");
>>> +               return -ENOMEM;
>>> +       }
>>> +
>>> +       priv = netdev_priv(netdev);
>>> +
>>> +       priv->udev = usbdev;
>>> +       priv->netdev = netdev;
>>> +       priv->usb_ka_first_pass = true;
>>> +       priv->can_ka_first_pass = true;
>>> +
>>> +       init_usb_anchor(&priv->rx_submitted);
>>> +       init_usb_anchor(&priv->tx_submitted);
>>> +
>>> +       usb_set_intfdata(intf, priv);
>>> +
>>> +       err = mcba_usb_start(priv);
>>> +       if (err) {
>>> +               if (err == -ENODEV)
>>> +                       netif_device_detach(priv->netdev);
>>> +
>>> +               netdev_warn(netdev, "couldn't start device: %d\n", err);
>>> +
>>> +               goto cleanup_candev;
>>> +       }
>>> +
>>> +       /* Init CAN device */
>>> +       priv->can.state = CAN_STATE_STOPPED;
>>> +       priv->can.clock.freq = MCBA_CAN_CLOCK;
>>> +       priv->can.bittiming_const = &mcba_bittiming_const;
>>> +       priv->can.do_set_mode = mcba_net_set_mode;
>>> +       priv->can.do_get_berr_counter = mcba_net_get_berr_counter;
>>> +       priv->can.do_set_bittiming = mcba_net_set_bittiming;
>>> +       priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
>>> +                       CAN_CTRLMODE_LISTENONLY |
>>> +                       CAN_CTRLMODE_ONE_SHOT;
>>> +
>>> +       netdev->netdev_ops = &mcba_netdev_ops;
>>> +
>>> +       netdev->flags |= IFF_ECHO; /* we support local echo */
>>> +
>>> +       SET_NETDEV_DEV(netdev, &intf->dev);
>>> +
>>> +       err = register_candev(netdev);
>>> +       if (err) {
>>> +               netdev_err(netdev,
>>> +                          "couldn't register CAN device: %d\n", err);
>>> +               goto cleanup_candev;
>>> +       }
>>> +
>>> +       err = device_create_file(&netdev->dev, &termination_attr);
>>> +       if (err)
>>> +               goto cleanup_unregister_candev;
>>> +
>>> +       return err;
>>> +
>>> +cleanup_unregister_candev:
>>> +       unregister_candev(netdev);
>>> +
>>> +cleanup_candev:
>>> +       free_candev(netdev);
>>> +
>>> +       return err;
>>> +}
>>> +
>>> +/* Called by the usb core when driver is unloaded or device is removed
>>> */
>>> +static void mcba_usb_disconnect(struct usb_interface *intf)
>>> +{
>>> +       struct mcba_priv *priv = usb_get_intfdata(intf);
>>> +
>>> +       device_remove_file(&priv->netdev->dev, &termination_attr);
>>> +
>>> +       usb_set_intfdata(intf, NULL);
>>> +
>>> +       if (priv) {
>>> +               netdev_info(priv->netdev, "device disconnected\n");
>>> +
>>> +               unregister_candev(priv->netdev);
>>> +               free_candev(priv->netdev);
>>> +
>>> +               mcba_urb_unlink(priv);
>>> +       }
>>> +}
>>> +
>>> +static struct usb_driver mcba_usb_driver = {
>>> +       .name =         MCBA_MODULE_NAME,
>>> +       .probe =        mcba_usb_probe,
>>> +       .disconnect =   mcba_usb_disconnect,
>>> +       .id_table =     mcba_usb_table,
>>> +};
>>> +
>>> +module_usb_driver(mcba_usb_driver);
>>> +
>>> +MODULE_AUTHOR("Remigiusz Kołłątaj <remigiusz.kollataj@mobica.com>");
>>> +MODULE_DESCRIPTION("SocketCAN driver for Microchip CAN BUS Analyzer
>>> Tool");
>>> +MODULE_LICENSE("GPL v2");
>>> --
>>> 2.10.1
>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-can" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>

^ permalink raw reply

* Re: [PATCH] can: mcba_usb: Add support for Microchip CAN BUS Analyzer.
From: Oliver Hartkopp @ 2016-12-20 18:34 UTC (permalink / raw)
  To: Kołłątaj, Remigiusz, linux-can
In-Reply-To: <CAKT-o-c8vxCtmQnc+erAZ1fKr7++cMj0hLFhNTkj6Q92aph-oQ@mail.gmail.com>

Hi Remik,

On 12/20/2016 07:55 AM, Kołłątaj, Remigiusz wrote:
> Could someone tell me what happened with the patch below? Is there
> something more I shall do with it?

I'm generally fine with the patch when the debug stuff is removed and we 
agreed on a common netlink interface to configure the termination.

Unfortunately I did not get any feedback on the last post regarding the 
termination API:

	http://marc.info/?l=linux-can&m=148008356719857&w=2

Regards,
Oliver

> On 4 November 2016 at 09:03, Remigiusz Kollataj
> <remigiusz.kollataj@mobica.com> wrote:
>>
>> SocketCAN driver for Microchip CAN BUS Analyzer
>> (http://www.microchip.com/development-tools/)
>>
>> Main features (v1):
>> * Basic and extended frames support
>> * RTR frames support
>> * Supported hard-coded speeds: 20000, 33333, 50000, 80000, 83333, 100000,
>>   125000, 150000, 175000, 200000, 225000, 250000, 275000, 300000, 500000,
>>   625000, 800000, 1000000.
>> * Configuration of built-in termination via sysfs
>>
>> Signed-off-by: Remigiusz Kollataj <remigiusz.kollataj@mobica.com>
>> ---
>>  drivers/net/can/usb/Kconfig    |    6 +
>>  drivers/net/can/usb/Makefile   |    1 +
>>  drivers/net/can/usb/mcba_usb.c | 1210 ++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 1217 insertions(+)
>>  create mode 100644 drivers/net/can/usb/mcba_usb.c
>>
>> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
>> index 8483a40..2d0313e 100644
>> --- a/drivers/net/can/usb/Kconfig
>> +++ b/drivers/net/can/usb/Kconfig
>> @@ -81,4 +81,10 @@ config CAN_8DEV_USB
>>           This driver supports the USB2CAN interface
>>           from 8 devices (http://www.8devices.com).
>>
>> +config CAN_MCBA_USB
>> +       tristate "Microchip CAN BUS Analyzer interface"
>> +       ---help---
>> +         This driver supports the CAN BUS Analyzer interface
>> +         from Microchip (http://www.microchip.com/development-tools/).
>> +
>>  endmenu
>> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
>> index a64cf98..164453f 100644
>> --- a/drivers/net/can/usb/Makefile
>> +++ b/drivers/net/can/usb/Makefile
>> @@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
>>  obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
>>  obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
>>  obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
>> +obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o
>> diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c
>> new file mode 100644
>> index 0000000..a4fa8ba
>> --- /dev/null
>> +++ b/drivers/net/can/usb/mcba_usb.c
>> @@ -0,0 +1,1210 @@
>> +/* SocketCAN driver for Microchip CAN BUS Analyzer Tool
>> + *
>> + * Copyright (C) 2016 Mobica Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License as published
>> + * by the Free Software Foundation; version 2 of the License.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along
>> + * with this program.
>> + *
>> + * This driver is inspired by the 4.6.2 version of net/can/usb/usb_8dev.c
>> + */
>> +
>> +#include <linux/usb.h>
>> +#include <linux/can/dev.h>
>> +
>> +/* vendor and product id */
>> +#define MCBA_MODULE_NAME         "mcba_usb"
>> +#define MCBA_VENDOR_ID           0x04d8
>> +#define MCBA_PRODUCT_ID          0x0a30
>> +
>> +/* driver constants */
>> +#define MCBA_MAX_RX_URBS         20
>> +#define MCBA_MAX_TX_URBS         20
>> +#define MCBA_CTX_FREE            MCBA_MAX_TX_URBS
>> +
>> +/* RX buffer must be bigger than msg size since at the
>> + * beggining USB messages are stacked.
>> + */
>> +#define MCBA_USB_RX_BUFF_SIZE    64
>> +#define MCBA_USB_TX_BUFF_SIZE    (sizeof(struct mcba_usb_msg))
>> +
>> +/* MCBA endpoint numbers */
>> +#define MCBA_USB_EP_IN           1
>> +#define MCBA_USB_EP_OUT          1
>> +
>> +/* Not required by driver itself as CANBUS is USB based
>> + * Used internally by candev for bitrate calculation
>> + */
>> +#define MCBA_CAN_CLOCK           40000000
>> +
>> +/* Microchip command id */
>> +#define MBCA_CMD_RECEIVE_MESSAGE                0xE3
>> +#define MBCA_CMD_I_AM_ALIVE_FROM_CAN            0xF5
>> +#define MBCA_CMD_I_AM_ALIVE_FROM_USB            0xF7
>> +#define MBCA_CMD_CHANGE_BIT_RATE                0xA1
>> +#define MBCA_CMD_TRANSMIT_MESSAGE_EV            0xA3
>> +#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE   0xA8
>> +#define MBCA_CMD_READ_FW_VERSION                0xA9
>> +#define MBCA_CMD_NOTHING_TO_SEND                0xFF
>> +#define MBCA_CMD_TRANSMIT_MESSAGE_RSP           0xE2
>> +
>> +/* debug module parameter handling */
>> +#define MCBA_PARAM_DEBUG_DISABLE    0
>> +#define MCBA_PARAM_DEBUG_USB        1
>> +#define MCBA_PARAM_DEBUG_CAN        2
>> +#define MCBA_IS_USB_DEBUG()         (debug & MCBA_PARAM_DEBUG_USB)
>> +#define MCBA_IS_CAN_DEBUG()         (debug & MCBA_PARAM_DEBUG_CAN)
>> +
>> +#define MCBA_VER_REQ_USB             1
>> +#define MCBA_VER_REQ_CAN             2
>> +
>> +#define MCBA_CAN_S_SID0_SID2_MASK    0x7
>> +#define MCBA_CAN_S_SID3_SID10_MASK   0x7F8
>> +#define MCBA_CAN_S_SID3_SID10_SHIFT  3
>> +
>> +#define MCBA_CAN_EID0_EID7_MASK      0xff
>> +#define MCBA_CAN_EID8_EID15_MASK     0xff00
>> +#define MCBA_CAN_EID16_EID17_MASK    0x30000
>> +#define MCBA_CAN_E_SID0_SID2_MASK    0x1c0000
>> +#define MCBA_CAN_E_SID3_SID10_MASK   0x1fe00000
>> +#define MCBA_CAN_EID8_EID15_SHIFT    8
>> +#define MCBA_CAN_EID16_EID17_SHIFT   16
>> +#define MCBA_CAN_E_SID0_SID2_SHIFT   18
>> +#define MCBA_CAN_E_SID3_SID10_SHIFT  21
>> +
>> +#define MCBA_SIDL_SID0_SID2_MASK     0xe0
>> +#define MCBA_SIDL_EXID_MASK          0x8
>> +#define MCBA_SIDL_EID16_EID17_MASK   0x3
>> +#define MCBA_SIDL_SID0_SID2_SHIFT    5
>> +
>> +#define MCBA_DLC_MASK                0xf
>> +#define MCBA_DLC_RTR_MASK            0x40
>> +
>> +#define MCBA_CAN_RTR_MASK            0x40000000
>> +#define MCBA_CAN_EXID_MASK           0x80000000
>> +
>> +#define MCBA_SET_S_SIDL(can_id)\
>> +(((can_id) & MCBA_CAN_S_SID0_SID2_MASK) << MCBA_SIDL_SID0_SID2_SHIFT)
>> +
>> +#define MCBA_SET_E_SIDL(can_id)\
>> +(((((can_id) & MCBA_CAN_E_SID0_SID2_MASK) >> MCBA_CAN_E_SID0_SID2_SHIFT)\
>> +<< MCBA_SIDL_SID0_SID2_SHIFT) |\
>> +(((can_id) & MCBA_CAN_EID16_EID17_MASK) >> MCBA_CAN_EID16_EID17_SHIFT) |\
>> +MCBA_SIDL_EXID_MASK)
>> +
>> +#define MCBA_SET_S_SIDH(can_id)\
>> +(((can_id) & MCBA_CAN_S_SID3_SID10_MASK) >> MCBA_CAN_S_SID3_SID10_SHIFT)
>> +
>> +#define MCBA_SET_E_SIDH(can_id)\
>> +(((can_id) & MCBA_CAN_E_SID3_SID10_MASK) >> MCBA_CAN_E_SID3_SID10_SHIFT)
>> +
>> +#define MCBA_SET_EIDL(can_id)\
>> +((can_id) & MCBA_CAN_EID0_EID7_MASK)
>> +
>> +#define MCBA_SET_EIDH(can_id)\
>> +(((can_id) & MCBA_CAN_EID8_EID15_MASK) >> MCBA_CAN_EID8_EID15_SHIFT)
>> +
>> +#define MCBA_CAN_GET_SID(usb_msg)\
>> +((((usb_msg)->sidl & MCBA_SIDL_SID0_SID2_MASK) >> MCBA_SIDL_SID0_SID2_SHIFT) |\
>> +((usb_msg)->sidh << MCBA_CAN_S_SID3_SID10_SHIFT))
>> +
>> +#define MCBA_CAN_GET_EID(usb_msg)\
>> +((((usb_msg)->sidh << MCBA_CAN_E_SID3_SID10_SHIFT) |\
>> +(((usb_msg)->sidl & MCBA_SIDL_SID0_SID2_MASK) >> MCBA_SIDL_SID0_SID2_SHIFT) \
>> +<< MCBA_CAN_E_SID0_SID2_SHIFT) |\
>> +(((usb_msg)->sidl & MCBA_SIDL_EID16_EID17_MASK) \
>> +<< MCBA_CAN_EID16_EID17_SHIFT) |\
>> +((usb_msg)->eidh << MCBA_CAN_EID8_EID15_SHIFT) |\
>> +(usb_msg)->eidl |\
>> +MCBA_CAN_EXID_MASK)
>> +
>> +#define MCBA_RX_IS_EXID(usb_msg)    ((usb_msg)->sidl & MCBA_SIDL_EXID_MASK)
>> +#define MCBA_RX_IS_RTR(usb_msg)     ((usb_msg)->dlc & MCBA_DLC_RTR_MASK)
>> +#define MCBA_TX_IS_EXID(can_frame)  ((can_frame)->can_id & MCBA_CAN_EXID_MASK)
>> +#define MCBA_TX_IS_RTR(can_frame)   ((can_frame)->can_id & MCBA_CAN_RTR_MASK)
>> +
>> +struct mcba_usb_ctx {
>> +       struct mcba_priv *priv;
>> +       u32 ndx;
>> +       u8 dlc;
>> +       bool can;
>> +};
>> +
>> +/* Structure to hold all of our device specific stuff */
>> +struct mcba_priv {
>> +       struct can_priv can; /* must be the first member */
>> +       struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
>> +       struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
>> +
>> +       struct usb_device *udev;
>> +       struct net_device *netdev;
>> +       struct usb_anchor tx_submitted;
>> +       struct usb_anchor rx_submitted;
>> +       struct can_berr_counter bec;
>> +       u8 termination_state;
>> +       bool usb_ka_first_pass;
>> +       bool can_ka_first_pass;
>> +};
>> +
>> +/* command frame */
>> +struct __packed mcba_usb_msg_can {
>> +       u8 cmd_id;
>> +       u8 eidh;
>> +       u8 eidl;
>> +       u8 sidh;
>> +       u8 sidl;
>> +       u8 dlc;
>> +       u8 data[8];
>> +       u8 timestamp[4];
>> +       u8 checksum;
>> +};
>> +
>> +/* command frame */
>> +struct __packed mcba_usb_msg {
>> +       u8 cmd_id;
>> +       u8 unused[18];
>> +};
>> +
>> +struct __packed mcba_usb_msg_ka_usb {
>> +       u8 cmd_id;
>> +       u8 termination_state;
>> +       u8 soft_ver_major;
>> +       u8 soft_ver_minor;
>> +       u8 unused[15];
>> +};
>> +
>> +struct __packed mcba_usb_msg_ka_can {
>> +       u8 cmd_id;
>> +       u8 tx_err_cnt;
>> +       u8 rx_err_cnt;
>> +       u8 rx_buff_ovfl;
>> +       u8 tx_bus_off;
>> +       u8 can_bitrate_hi;
>> +       u8 can_bitrate_lo;
>> +       u8 rx_lost_lo;
>> +       u8 rx_lost_hi;
>> +       u8 can_stat;
>> +       u8 soft_ver_major;
>> +       u8 soft_ver_minor;
>> +       u8 debug_mode;
>> +       u8 test_complete;
>> +       u8 test_result;
>> +       u8 unused[4];
>> +};
>> +
>> +struct __packed mcba_usb_msg_change_bitrate {
>> +       u8 cmd_id;
>> +       u8 bitrate_hi;
>> +       u8 bitrate_lo;
>> +       u8 unused[16];
>> +};
>> +
>> +struct __packed mcba_usb_msg_terminaton {
>> +       u8 cmd_id;
>> +       u8 termination;
>> +       u8 unused[17];
>> +};
>> +
>> +struct __packed mcba_usb_msg_fw_ver {
>> +       u8 cmd_id;
>> +       u8 pic;
>> +       u8 unused[17];
>> +};
>> +
>> +struct bitrate_settings {
>> +       struct can_bittiming bt;
>> +       u16 kbps;
>> +};
>> +
>> +/* Required by can-dev but not for the sake of driver as CANBUS is USB based */
>> +static const struct can_bittiming_const mcba_bittiming_const = {
>> +       .name = "mcba_usb",
>> +       .tseg1_min = 1,
>> +       .tseg1_max = 8,
>> +       .tseg2_min = 1,
>> +       .tseg2_max = 8,
>> +       .sjw_max = 4,
>> +       .brp_min = 2,
>> +       .brp_max = 128,
>> +       .brp_inc = 2,
>> +};
>> +
>> +/* predefined values hardcoded in device's firmware */
>> +static const struct bitrate_settings br_settings[] = {
>> +       {
>> +               .bt = {
>> +                       .bitrate = 19940,
>> +                       .sample_point = 700,
>> +                       .tq = 2500,
>> +                       .prop_seg = 5,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 6,
>> +                       .sjw = 1,
>> +                       .brp = 100,
>> +               },
>> +               .kbps = 20
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 33333,
>> +                       .sample_point = 680,
>> +                       .tq = 1200,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 8,
>> +                       .sjw = 1,
>> +                       .brp = 48,
>> +               },
>> +               .kbps = 33
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 50000,
>> +                       .sample_point = 800,
>> +                       .tq = 1000,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 7,
>> +                       .phase_seg2 = 4,
>> +                       .sjw = 1,
>> +                       .brp = 40,
>> +               },
>> +               .kbps = 50
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 80000,
>> +                       .sample_point = 680,
>> +                       .tq = 500,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 8,
>> +                       .sjw = 1,
>> +                       .brp = 20,
>> +               },
>> +               .kbps = 80
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 83333,
>> +                       .sample_point = 708,
>> +                       .tq = 500,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 7,
>> +                       .sjw = 1,
>> +                       .brp = 20,
>> +               },
>> +               .kbps = 83
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 100000,
>> +                       .sample_point = 700,
>> +                       .tq = 1000,
>> +                       .prop_seg = 1,
>> +                       .phase_seg1 = 5,
>> +                       .phase_seg2 = 3,
>> +                       .sjw = 1,
>> +                       .brp = 40,
>> +               },
>> +               .kbps = 100
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 125000,
>> +                       .sample_point = 600,
>> +                       .tq = 400,
>> +                       .prop_seg = 3,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 8,
>> +                       .sjw = 1,
>> +                       .brp = 16,
>> +               },
>> +               .kbps = 125
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 150375,
>> +                       .sample_point = 789,
>> +                       .tq = 350,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 6,
>> +                       .phase_seg2 = 4,
>> +                       .sjw = 1,
>> +                       .brp = 14,
>> +               },
>> +               .kbps = 150
>> +       },
>> +
>> +       {
>> +               .bt = {
>> +                       .bitrate = 175438,
>> +                       .sample_point = 789,
>> +                       .tq = 300,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 6,
>> +                       .phase_seg2 = 4,
>> +                       .sjw = 1,
>> +                       .brp = 12,
>> +               },
>> +               .kbps = 175
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 200000,
>> +                       .sample_point = 680,
>> +                       .tq = 200,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 8,
>> +                       .sjw = 1,
>> +                       .brp = 8,
>> +               },
>> +               .kbps = 200
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 227272,
>> +                       .sample_point = 772,
>> +                       .tq = 200,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 5,
>> +                       .sjw = 1,
>> +                       .brp = 8,
>> +               },
>> +               .kbps = 225
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 250000,
>> +                       .sample_point = 600,
>> +                       .tq = 200,
>> +                       .prop_seg = 3,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 8,
>> +                       .sjw = 1,
>> +                       .brp = 8,
>> +               },
>> +               .kbps = 250
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 277777,
>> +                       .sample_point = 708,
>> +                       .tq = 150,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 7,
>> +                       .sjw = 1,
>> +                       .brp = 6,
>> +               },
>> +               .kbps = 275
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 303030,
>> +                       .sample_point = 772,
>> +                       .tq = 150,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 5,
>> +                       .sjw = 1,
>> +                       .brp = 6,
>> +               },
>> +               .kbps = 300
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 500000,
>> +                       .sample_point = 600,
>> +                       .tq = 100,
>> +                       .prop_seg = 3,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 8,
>> +                       .sjw = 1,
>> +                       .brp = 4,
>> +               },
>> +               .kbps = 500
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 625000,
>> +                       .sample_point = 750,
>> +                       .tq = 200,
>> +                       .prop_seg = 1,
>> +                       .phase_seg1 = 4,
>> +                       .phase_seg2 = 2,
>> +                       .sjw = 1,
>> +                       .brp = 8,
>> +               },
>> +               .kbps = 625
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 800000,
>> +                       .sample_point = 680,
>> +                       .tq = 50,
>> +                       .prop_seg = 8,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 8,
>> +                       .sjw = 1,
>> +                       .brp = 2,
>> +               },
>> +               .kbps = 800
>> +       },
>> +       {
>> +               .bt = {
>> +                       .bitrate = 1000000,
>> +                       .sample_point = 600,
>> +                       .tq = 50,
>> +                       .prop_seg = 3,
>> +                       .phase_seg1 = 8,
>> +                       .phase_seg2 = 8,
>> +                       .sjw = 1,
>> +                       .brp = 2,
>> +               },
>> +               .kbps = 1000
>> +       }
>> +};
>> +
>> +static int debug;
>> +module_param(debug, int, 0664);
>> +MODULE_PARM_DESC(debug,
>> +                "Binary flag to control device debug (keep alive) prints in dmesg. 0='Debug prints disabled' "
>> +                __stringify(MCBA_PARAM_DEBUG_USB) "='PIC_USB debugs enabled' "
>> +                __stringify(MCBA_PARAM_DEBUG_CAN) "='PIC_CAN debugs enabled'");
>> +
>> +static const struct usb_device_id mcba_usb_table[] = {
>> +       { USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
>> +       { } /* Terminating entry */
>> +};
>> +
>> +MODULE_DEVICE_TABLE(usb, mcba_usb_table);
>> +
>> +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv,
>> +                                struct mcba_usb_msg *usb_msg,
>> +                                struct sk_buff *skb);
>> +static void mcba_usb_xmit_cmd(struct mcba_priv *priv,
>> +                             struct mcba_usb_msg *usb_msg);
>> +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic);
>> +static void mcba_usb_xmit_termination(struct mcba_priv *priv, u8 termination);
>> +static inline void mcba_init_ctx(struct mcba_priv *priv);
>> +
>> +static ssize_t termination_show(struct device *dev,
>> +                               struct device_attribute *attr, char *buf)
>> +{
>> +       struct net_device *netdev = to_net_dev(dev);
>> +       struct mcba_priv *priv = netdev_priv(netdev);
>> +
>> +       return sprintf(buf, "%hhu\n", priv->termination_state);
>> +}
>> +
>> +static ssize_t termination_store(struct device *dev,
>> +                                struct device_attribute *attr,
>> +                                const char *buf, size_t count)
>> +{
>> +       struct net_device *netdev = to_net_dev(dev);
>> +       struct mcba_priv *priv = netdev_priv(netdev);
>> +       int tmp = -1;
>> +       int ret = -1;
>> +
>> +       ret = kstrtoint(buf, 10, &tmp);
>> +
>> +       if ((ret == 0) && ((tmp == 0) || (tmp == 1))) {
>> +               priv->termination_state = tmp;
>> +               mcba_usb_xmit_termination(priv, priv->termination_state);
>> +       }
>> +
>> +       return count;
>> +}
>> +
>> +static struct device_attribute termination_attr = {
>> +       .attr = {
>> +               .name = "termination",
>> +               .mode = 0666 },
>> +       .show   = termination_show,
>> +       .store  = termination_store
>> +};
>> +
>> +static void mcba_usb_process_can(struct mcba_priv *priv,
>> +                                struct mcba_usb_msg_can *msg)
>> +{
>> +       struct can_frame *cf;
>> +       struct sk_buff *skb;
>> +       struct net_device_stats *stats = &priv->netdev->stats;
>> +
>> +       skb = alloc_can_skb(priv->netdev, &cf);
>> +       if (!skb)
>> +               return;
>> +
>> +       if (MCBA_RX_IS_EXID(msg))
>> +               cf->can_id = MCBA_CAN_GET_EID(msg);
>> +       else
>> +               cf->can_id = MCBA_CAN_GET_SID(msg);
>> +
>> +       if (MCBA_RX_IS_RTR(msg))
>> +               cf->can_id |= MCBA_CAN_RTR_MASK;
>> +
>> +       cf->can_dlc = msg->dlc & MCBA_DLC_MASK;
>> +
>> +       memcpy(cf->data, msg->data, cf->can_dlc);
>> +
>> +       stats->rx_packets++;
>> +       stats->rx_bytes += cf->can_dlc;
>> +       netif_rx(skb);
>> +}
>> +
>> +static void mcba_usb_process_ka_usb(struct mcba_priv *priv,
>> +                                   struct mcba_usb_msg_ka_usb *msg)
>> +{
>> +       if (unlikely(MCBA_IS_USB_DEBUG())) {
>> +               netdev_info(priv->netdev,
>> +                           "USB_KA: termination %hhu, ver_maj %hhu, soft_min %hhu\n",
>> +                           msg->termination_state, msg->soft_ver_major,
>> +                           msg->soft_ver_minor);
>> +       }
>> +
>> +       if (unlikely(priv->usb_ka_first_pass)) {
>> +               netdev_info(priv->netdev,
>> +                           "PIC USB version %hhu.%hhu\n",
>> +                           msg->soft_ver_major, msg->soft_ver_minor);
>> +
>> +               priv->usb_ka_first_pass = false;
>> +       }
>> +
>> +       priv->termination_state = msg->termination_state;
>> +}
>> +
>> +static void mcba_usb_process_ka_can(struct mcba_priv *priv,
>> +                                   struct mcba_usb_msg_ka_can *msg)
>> +{
>> +       if (unlikely(MCBA_IS_CAN_DEBUG())) {
>> +               netdev_info(priv->netdev,
>> +                           "CAN_KA: tx_err_cnt %hhu, rx_err_cnt %hhu, rx_buff_ovfl %hhu, tx_bus_off %hhu, can_bitrate %hu, rx_lost %hu, can_stat %hhu, soft_ver %hhu.%hhu, debug_mode %hhu, test_complete %hhu, test_result %hhu\n",
>> +                           msg->tx_err_cnt, msg->rx_err_cnt, msg->rx_buff_ovfl,
>> +                           msg->tx_bus_off,
>> +                           ((msg->can_bitrate_hi << 8) + msg->can_bitrate_lo),
>> +                           ((msg->rx_lost_hi >> 8) + msg->rx_lost_lo),
>> +                           msg->can_stat, msg->soft_ver_major,
>> +                           msg->soft_ver_minor,
>> +                           msg->debug_mode, msg->test_complete,
>> +                           msg->test_result);
>> +       }
>> +
>> +       if (unlikely(priv->can_ka_first_pass)) {
>> +               netdev_info(priv->netdev,
>> +                           "PIC CAN version %hhu.%hhu\n",
>> +                           msg->soft_ver_major, msg->soft_ver_minor);
>> +
>> +               priv->can_ka_first_pass = false;
>> +       }
>> +
>> +       priv->bec.txerr = msg->tx_err_cnt;
>> +       priv->bec.rxerr = msg->rx_err_cnt;
>> +}
>> +
>> +static void mcba_usb_process_rx(struct mcba_priv *priv,
>> +                               struct mcba_usb_msg *msg)
>> +{
>> +       switch (msg->cmd_id) {
>> +       case MBCA_CMD_I_AM_ALIVE_FROM_CAN:
>> +               mcba_usb_process_ka_can(priv,
>> +                                       (struct mcba_usb_msg_ka_can *)msg);
>> +               break;
>> +
>> +       case MBCA_CMD_I_AM_ALIVE_FROM_USB:
>> +               mcba_usb_process_ka_usb(priv,
>> +                                       (struct mcba_usb_msg_ka_usb *)msg);
>> +               break;
>> +
>> +       case MBCA_CMD_RECEIVE_MESSAGE:
>> +               mcba_usb_process_can(priv, (struct mcba_usb_msg_can *)msg);
>> +               break;
>> +
>> +       case MBCA_CMD_NOTHING_TO_SEND:
>> +               /* Side effect of communication between PIC_USB and PIC_CAN.
>> +                * PIC_CAN is telling us that it has nothing to send
>> +                */
>> +               break;
>> +
>> +       case MBCA_CMD_TRANSMIT_MESSAGE_RSP:
>> +               /* Transmission response from the device containing timestamp */
>> +               break;
>> +
>> +       default:
>> +               netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)",
>> +                           msg->cmd_id);
>> +               break;
>> +       }
>> +}
>> +
>> +/* Callback for reading data from device
>> + *
>> + * Check urb status, call read function and resubmit urb read operation.
>> + */
>> +static void mcba_usb_read_bulk_callback(struct urb *urb)
>> +{
>> +       struct mcba_priv *priv = urb->context;
>> +       struct net_device *netdev;
>> +       int retval;
>> +       int pos = 0;
>> +
>> +       netdev = priv->netdev;
>> +
>> +       if (!netif_device_present(netdev))
>> +               return;
>> +
>> +       switch (urb->status) {
>> +       case 0: /* success */
>> +               break;
>> +
>> +       case -ENOENT:
>> +       case -ESHUTDOWN:
>> +               return;
>> +
>> +       default:
>> +               netdev_info(netdev, "Rx URB aborted (%d)\n",
>> +                           urb->status);
>> +
>> +               goto resubmit_urb;
>> +       }
>> +
>> +       while (pos < urb->actual_length) {
>> +               struct mcba_usb_msg *msg;
>> +
>> +               if (pos + sizeof(struct mcba_usb_msg) > urb->actual_length) {
>> +                       netdev_err(priv->netdev, "format error\n");
>> +                       break;
>> +               }
>> +
>> +               msg = (struct mcba_usb_msg *)(urb->transfer_buffer + pos);
>> +               mcba_usb_process_rx(priv, msg);
>> +
>> +               pos += sizeof(struct mcba_usb_msg);
>> +       }
>> +
>> +resubmit_urb:
>> +
>> +       usb_fill_bulk_urb(urb, priv->udev,
>> +                         usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_OUT),
>> +                         urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE,
>> +                         mcba_usb_read_bulk_callback, priv);
>> +
>> +       retval = usb_submit_urb(urb, GFP_ATOMIC);
>> +
>> +       if (retval == -ENODEV)
>> +               netif_device_detach(netdev);
>> +       else if (retval)
>> +               netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
>> +                          retval);
>> +}
>> +
>> +/* Start USB device */
>> +static int mcba_usb_start(struct mcba_priv *priv)
>> +{
>> +       struct net_device *netdev = priv->netdev;
>> +       int err, i;
>> +
>> +       for (i = 0; i < MCBA_MAX_RX_URBS; i++) {
>> +               struct urb *urb = NULL;
>> +               u8 *buf;
>> +
>> +               /* create a URB, and a buffer for it */
>> +               urb = usb_alloc_urb(0, GFP_KERNEL);
>> +               if (!urb) {
>> +                       netdev_err(netdev, "No memory left for URBs\n");
>> +                       err = -ENOMEM;
>> +                       break;
>> +               }
>> +
>> +               buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
>> +                                        GFP_KERNEL,
>> +                                        &urb->transfer_dma);
>> +               if (!buf) {
>> +                       netdev_err(netdev, "No memory left for USB buffer\n");
>> +                       usb_free_urb(urb);
>> +                       err = -ENOMEM;
>> +                       break;
>> +               }
>> +
>> +               usb_fill_bulk_urb(urb, priv->udev,
>> +                                 usb_rcvbulkpipe(priv->udev,
>> +                                                 MCBA_USB_EP_IN),
>> +                                 buf, MCBA_USB_RX_BUFF_SIZE,
>> +                                 mcba_usb_read_bulk_callback, priv);
>> +               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
>> +               usb_anchor_urb(urb, &priv->rx_submitted);
>> +
>> +               err = usb_submit_urb(urb, GFP_KERNEL);
>> +               if (err) {
>> +                       usb_unanchor_urb(urb);
>> +                       usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
>> +                                         buf, urb->transfer_dma);
>> +                       usb_free_urb(urb);
>> +                       break;
>> +               }
>> +
>> +               /* Drop reference, USB core will take care of freeing it */
>> +               usb_free_urb(urb);
>> +       }
>> +
>> +       /* Did we submit any URBs */
>> +       if (i == 0) {
>> +               netdev_warn(netdev, "couldn't setup read URBs\n");
>> +               return err;
>> +       }
>> +
>> +       /* Warn if we've couldn't transmit all the URBs */
>> +       if (i < MCBA_MAX_RX_URBS)
>> +               netdev_warn(netdev, "rx performance may be slow\n");
>> +
>> +       priv->can.state = CAN_STATE_ERROR_ACTIVE;
>> +
>> +       mcba_init_ctx(priv);
>> +       mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB);
>> +       mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN);
>> +
>> +       return err;
>> +}
>> +
>> +static inline void mcba_init_ctx(struct mcba_priv *priv)
>> +{
>> +       int i = 0;
>> +
>> +       for (i = 0; i < MCBA_MAX_TX_URBS; i++)
>> +               priv->tx_context[i].ndx = MCBA_CTX_FREE;
>> +}
>> +
>> +static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv)
>> +{
>> +       int i = 0;
>> +       struct mcba_usb_ctx *ctx = 0;
>> +
>> +       for (i = 0; i < MCBA_MAX_TX_URBS; i++) {
>> +               if (priv->tx_context[i].ndx == MCBA_CTX_FREE) {
>> +                       ctx = &priv->tx_context[i];
>> +                       ctx->ndx = i;
>> +                       ctx->priv = priv;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       return ctx;
>> +}
>> +
>> +static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx)
>> +{
>> +       ctx->ndx = MCBA_CTX_FREE;
>> +       ctx->priv = 0;
>> +       ctx->dlc = 0;
>> +       ctx->can = false;
>> +}
>> +
>> +static void mcba_usb_write_bulk_callback(struct urb *urb)
>> +{
>> +       struct mcba_usb_ctx *ctx = urb->context;
>> +       struct net_device *netdev;
>> +
>> +       WARN_ON(!ctx);
>> +
>> +       netdev = ctx->priv->netdev;
>> +
>> +       if (ctx->can) {
>> +               if (!netif_device_present(netdev))
>> +                       return;
>> +
>> +               netdev->stats.tx_packets++;
>> +               netdev->stats.tx_bytes += ctx->dlc;
>> +
>> +               can_get_echo_skb(netdev, ctx->ndx);
>> +
>> +               netif_wake_queue(netdev);
>> +       }
>> +
>> +       /* free up our allocated buffer */
>> +       usb_free_coherent(urb->dev, urb->transfer_buffer_length,
>> +                         urb->transfer_buffer, urb->transfer_dma);
>> +
>> +       if (urb->status)
>> +               netdev_info(netdev, "Tx URB aborted (%d)\n",
>> +                           urb->status);
>> +
>> +       /* Release context */
>> +       mcba_usb_free_ctx(ctx);
>> +}
>> +
>> +/* Send data to device */
>> +static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
>> +                                      struct net_device *netdev)
>> +{
>> +       struct mcba_priv *priv = netdev_priv(netdev);
>> +       struct can_frame *cf = (struct can_frame *)skb->data;
>> +       struct mcba_usb_msg_can usb_msg;
>> +
>> +       usb_msg.cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV;
>> +       memcpy(usb_msg.data, cf->data, sizeof(usb_msg.data));
>> +
>> +       if (MCBA_TX_IS_EXID(cf)) {
>> +               usb_msg.sidl = MCBA_SET_E_SIDL(cf->can_id);
>> +               usb_msg.sidh = MCBA_SET_E_SIDH(cf->can_id);
>> +               usb_msg.eidl = MCBA_SET_EIDL(cf->can_id);
>> +               usb_msg.eidh = MCBA_SET_EIDH(cf->can_id);
>> +       } else {
>> +               usb_msg.sidl = MCBA_SET_S_SIDL(cf->can_id);
>> +               usb_msg.sidh = MCBA_SET_S_SIDH(cf->can_id);
>> +               usb_msg.eidl = 0;
>> +               usb_msg.eidh = 0;
>> +       }
>> +
>> +       usb_msg.dlc = cf->can_dlc;
>> +
>> +       if (MCBA_TX_IS_RTR(cf))
>> +               usb_msg.dlc |= MCBA_DLC_RTR_MASK;
>> +
>> +       return mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, skb);
>> +}
>> +
>> +/* Send data to device */
>> +static void mcba_usb_xmit_cmd(struct mcba_priv *priv,
>> +                             struct mcba_usb_msg *usb_msg)
>> +{
>> +       mcba_usb_xmit(priv, usb_msg, 0);
>> +}
>> +
>> +/* Send data to device */
>> +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv,
>> +                                struct mcba_usb_msg *usb_msg,
>> +                                struct sk_buff *skb)
>> +{
>> +       struct net_device_stats *stats = &priv->netdev->stats;
>> +       struct mcba_usb_ctx *ctx = 0;
>> +       struct urb *urb;
>> +       u8 *buf;
>> +       int err;
>> +
>> +       ctx = mcba_usb_get_free_ctx(priv);
>> +       if (!ctx) {
>> +               /* Slow down tx path */
>> +               netif_stop_queue(priv->netdev);
>> +
>> +               return NETDEV_TX_BUSY;
>> +       }
>> +
>> +       if (skb) {
>> +               ctx->dlc = ((struct mcba_usb_msg_can *)usb_msg)->dlc
>> +                               & MCBA_DLC_MASK;
>> +               can_put_echo_skb(skb, priv->netdev, ctx->ndx);
>> +               ctx->can = true;
>> +       } else {
>> +               ctx->can = false;
>> +       }
>> +
>> +       /* create a URB, and a buffer for it, and copy the data to the URB */
>> +       urb = usb_alloc_urb(0, GFP_ATOMIC);
>> +       if (!urb) {
>> +               netdev_err(priv->netdev, "No memory left for URBs\n");
>> +               goto nomem;
>> +       }
>> +
>> +       buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, GFP_ATOMIC,
>> +                                &urb->transfer_dma);
>> +       if (!buf) {
>> +               netdev_err(priv->netdev, "No memory left for USB buffer\n");
>> +               goto nomembuf;
>> +       }
>> +
>> +       memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE);
>> +
>> +       usb_fill_bulk_urb(urb, priv->udev,
>> +                         usb_sndbulkpipe(priv->udev, MCBA_USB_EP_OUT), buf,
>> +                         MCBA_USB_TX_BUFF_SIZE, mcba_usb_write_bulk_callback,
>> +                         ctx);
>> +
>> +       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
>> +       usb_anchor_urb(urb, &priv->tx_submitted);
>> +
>> +       err = usb_submit_urb(urb, GFP_ATOMIC);
>> +       if (unlikely(err))
>> +               goto failed;
>> +
>> +       /* Release our reference to this URB, the USB core will eventually free
>> +        * it entirely.
>> +        */
>> +       usb_free_urb(urb);
>> +
>> +       return NETDEV_TX_OK;
>> +
>> +failed:
>> +       usb_unanchor_urb(urb);
>> +       usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf,
>> +                         urb->transfer_dma);
>> +
>> +       if (err == -ENODEV)
>> +               netif_device_detach(priv->netdev);
>> +       else
>> +               netdev_warn(priv->netdev, "failed tx_urb %d\n", err);
>> +
>> +nomembuf:
>> +       usb_free_urb(urb);
>> +
>> +nomem:
>> +       can_free_echo_skb(priv->netdev, ctx->ndx);
>> +       dev_kfree_skb(skb);
>> +       stats->tx_dropped++;
>> +
>> +       return NETDEV_TX_OK;
>> +}
>> +
>> +static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16 bitrate)
>> +{
>> +       struct mcba_usb_msg_change_bitrate usb_msg;
>> +
>> +       usb_msg.cmd_id =  MBCA_CMD_CHANGE_BIT_RATE;
>> +       usb_msg.bitrate_hi = (0xff00 & bitrate) >> 8;
>> +       usb_msg.bitrate_lo = (0xff & bitrate);
>> +
>> +       mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
>> +}
>> +
>> +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic)
>> +{
>> +       struct mcba_usb_msg_fw_ver usb_msg;
>> +
>> +       usb_msg.cmd_id = MBCA_CMD_READ_FW_VERSION;
>> +       usb_msg.pic = pic;
>> +
>> +       mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
>> +}
>> +
>> +static void mcba_usb_xmit_termination(struct mcba_priv *priv, u8 termination)
>> +{
>> +       struct mcba_usb_msg_terminaton usb_msg;
>> +
>> +       usb_msg.cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE;
>> +       usb_msg.termination = termination;
>> +
>> +       mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
>> +}
>> +
>> +/* Open USB device */
>> +static int mcba_usb_open(struct net_device *netdev)
>> +{
>> +       int err;
>> +
>> +       /* common open */
>> +       err = open_candev(netdev);
>> +       if (err)
>> +               return err;
>> +
>> +       can_led_event(netdev, CAN_LED_EVENT_OPEN);
>> +
>> +       netif_start_queue(netdev);
>> +
>> +       return 0;
>> +}
>> +
>> +static void mcba_urb_unlink(struct mcba_priv *priv)
>> +{
>> +       usb_kill_anchored_urbs(&priv->rx_submitted);
>> +       usb_kill_anchored_urbs(&priv->tx_submitted);
>> +}
>> +
>> +/* Close USB device */
>> +static int mcba_usb_close(struct net_device *netdev)
>> +{
>> +       struct mcba_priv *priv = netdev_priv(netdev);
>> +
>> +       priv->can.state = CAN_STATE_STOPPED;
>> +
>> +       netif_stop_queue(netdev);
>> +
>> +       /* Stop polling */
>> +       mcba_urb_unlink(priv);
>> +
>> +       close_candev(netdev);
>> +
>> +       can_led_event(netdev, CAN_LED_EVENT_STOP);
>> +
>> +       return 0;
>> +}
>> +
>> +/* Set network device mode
>> + *
>> + * Maybe we should leave this function empty, because the device
>> + * set mode variable with open command.
>> + */
>> +static int mcba_net_set_mode(struct net_device *netdev, enum can_mode mode)
>> +{
>> +       return 0;
>> +}
>> +
>> +static int mcba_net_get_berr_counter(const struct net_device *netdev,
>> +                                    struct can_berr_counter *bec)
>> +{
>> +       struct mcba_priv *priv = netdev_priv(netdev);
>> +
>> +       bec->txerr = priv->bec.txerr;
>> +       bec->rxerr = priv->bec.rxerr;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct net_device_ops mcba_netdev_ops = {
>> +       .ndo_open = mcba_usb_open,
>> +       .ndo_stop = mcba_usb_close,
>> +       .ndo_start_xmit = mcba_usb_start_xmit
>> +};
>> +
>> +/* Microchip CANBUS has hardcoded bittiming values by default.
>> + * This function sends request via USB to change the speed and align bittiming
>> + * values for presentation purposes only
>> + */
>> +static int mcba_net_set_bittiming(struct net_device *netdev)
>> +{
>> +       u8 i;
>> +       struct mcba_priv *priv = netdev_priv(netdev);
>> +       struct can_bittiming *bt = &priv->can.bittiming;
>> +       const struct bitrate_settings *settings = 0;
>> +       const u8 setting_cnt = sizeof(br_settings) /
>> +                              sizeof(struct bitrate_settings);
>> +
>> +       for (i = 0; i < setting_cnt; ++i)
>> +               if (br_settings[i].bt.bitrate == bt->bitrate)
>> +                       settings = &br_settings[i];
>> +
>> +       if (settings) {
>> +               memcpy(bt, &settings->bt, sizeof(struct can_bittiming));
>> +
>> +               /* recalculate bitrate as it may be different than default */
>> +               bt->bitrate = 1000000000 / ((bt->sjw + bt->prop_seg +
>> +                                           bt->phase_seg1 + bt->phase_seg2) *
>> +                                           bt->tq);
>> +
>> +               mcba_usb_xmit_change_bitrate(priv, settings->kbps);
>> +       } else {
>> +               netdev_err(netdev, "Unsupported bittrate (%u). Use one of: 20000, 33333, 50000, 80000, 83333, 100000, 125000, 150000, 175000, 200000, 225000, 250000, 275000, 300000, 500000, 625000, 800000, 1000000\n",
>> +                          bt->bitrate);
>> +
>> +               return -EINVAL;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int mcba_usb_probe(struct usb_interface *intf,
>> +                         const struct usb_device_id *id)
>> +{
>> +       struct net_device *netdev;
>> +       struct mcba_priv *priv;
>> +       int err = -ENOMEM;
>> +       struct usb_device *usbdev = interface_to_usbdev(intf);
>> +
>> +       dev_info(&intf->dev, "Microchip CAN BUS analizer connected\n");
>> +
>> +       netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS);
>> +       if (!netdev) {
>> +               dev_err(&intf->dev, "Couldn't alloc candev\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       priv = netdev_priv(netdev);
>> +
>> +       priv->udev = usbdev;
>> +       priv->netdev = netdev;
>> +       priv->usb_ka_first_pass = true;
>> +       priv->can_ka_first_pass = true;
>> +
>> +       init_usb_anchor(&priv->rx_submitted);
>> +       init_usb_anchor(&priv->tx_submitted);
>> +
>> +       usb_set_intfdata(intf, priv);
>> +
>> +       err = mcba_usb_start(priv);
>> +       if (err) {
>> +               if (err == -ENODEV)
>> +                       netif_device_detach(priv->netdev);
>> +
>> +               netdev_warn(netdev, "couldn't start device: %d\n", err);
>> +
>> +               goto cleanup_candev;
>> +       }
>> +
>> +       /* Init CAN device */
>> +       priv->can.state = CAN_STATE_STOPPED;
>> +       priv->can.clock.freq = MCBA_CAN_CLOCK;
>> +       priv->can.bittiming_const = &mcba_bittiming_const;
>> +       priv->can.do_set_mode = mcba_net_set_mode;
>> +       priv->can.do_get_berr_counter = mcba_net_get_berr_counter;
>> +       priv->can.do_set_bittiming = mcba_net_set_bittiming;
>> +       priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
>> +                       CAN_CTRLMODE_LISTENONLY |
>> +                       CAN_CTRLMODE_ONE_SHOT;
>> +
>> +       netdev->netdev_ops = &mcba_netdev_ops;
>> +
>> +       netdev->flags |= IFF_ECHO; /* we support local echo */
>> +
>> +       SET_NETDEV_DEV(netdev, &intf->dev);
>> +
>> +       err = register_candev(netdev);
>> +       if (err) {
>> +               netdev_err(netdev,
>> +                          "couldn't register CAN device: %d\n", err);
>> +               goto cleanup_candev;
>> +       }
>> +
>> +       err = device_create_file(&netdev->dev, &termination_attr);
>> +       if (err)
>> +               goto cleanup_unregister_candev;
>> +
>> +       return err;
>> +
>> +cleanup_unregister_candev:
>> +       unregister_candev(netdev);
>> +
>> +cleanup_candev:
>> +       free_candev(netdev);
>> +
>> +       return err;
>> +}
>> +
>> +/* Called by the usb core when driver is unloaded or device is removed */
>> +static void mcba_usb_disconnect(struct usb_interface *intf)
>> +{
>> +       struct mcba_priv *priv = usb_get_intfdata(intf);
>> +
>> +       device_remove_file(&priv->netdev->dev, &termination_attr);
>> +
>> +       usb_set_intfdata(intf, NULL);
>> +
>> +       if (priv) {
>> +               netdev_info(priv->netdev, "device disconnected\n");
>> +
>> +               unregister_candev(priv->netdev);
>> +               free_candev(priv->netdev);
>> +
>> +               mcba_urb_unlink(priv);
>> +       }
>> +}
>> +
>> +static struct usb_driver mcba_usb_driver = {
>> +       .name =         MCBA_MODULE_NAME,
>> +       .probe =        mcba_usb_probe,
>> +       .disconnect =   mcba_usb_disconnect,
>> +       .id_table =     mcba_usb_table,
>> +};
>> +
>> +module_usb_driver(mcba_usb_driver);
>> +
>> +MODULE_AUTHOR("Remigiusz Kołłątaj <remigiusz.kollataj@mobica.com>");
>> +MODULE_DESCRIPTION("SocketCAN driver for Microchip CAN BUS Analyzer Tool");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 2.10.1
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-can" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: [PATCH] can: mcba_usb: Add support for Microchip CAN BUS Analyzer.
From: Kołłątaj, Remigiusz @ 2016-12-20  6:55 UTC (permalink / raw)
  To: linux-can
In-Reply-To: <20161104080357.780-1-remigiusz.kollataj@mobica.com>

Hi,

Could someone tell me what happened with the patch below? Is there
something more I shall do with it?


Regards,
Remik

On 4 November 2016 at 09:03, Remigiusz Kollataj
<remigiusz.kollataj@mobica.com> wrote:
>
> SocketCAN driver for Microchip CAN BUS Analyzer
> (http://www.microchip.com/development-tools/)
>
> Main features (v1):
> * Basic and extended frames support
> * RTR frames support
> * Supported hard-coded speeds: 20000, 33333, 50000, 80000, 83333, 100000,
>   125000, 150000, 175000, 200000, 225000, 250000, 275000, 300000, 500000,
>   625000, 800000, 1000000.
> * Configuration of built-in termination via sysfs
>
> Signed-off-by: Remigiusz Kollataj <remigiusz.kollataj@mobica.com>
> ---
>  drivers/net/can/usb/Kconfig    |    6 +
>  drivers/net/can/usb/Makefile   |    1 +
>  drivers/net/can/usb/mcba_usb.c | 1210 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1217 insertions(+)
>  create mode 100644 drivers/net/can/usb/mcba_usb.c
>
> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> index 8483a40..2d0313e 100644
> --- a/drivers/net/can/usb/Kconfig
> +++ b/drivers/net/can/usb/Kconfig
> @@ -81,4 +81,10 @@ config CAN_8DEV_USB
>           This driver supports the USB2CAN interface
>           from 8 devices (http://www.8devices.com).
>
> +config CAN_MCBA_USB
> +       tristate "Microchip CAN BUS Analyzer interface"
> +       ---help---
> +         This driver supports the CAN BUS Analyzer interface
> +         from Microchip (http://www.microchip.com/development-tools/).
> +
>  endmenu
> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> index a64cf98..164453f 100644
> --- a/drivers/net/can/usb/Makefile
> +++ b/drivers/net/can/usb/Makefile
> @@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
>  obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
>  obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
>  obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
> +obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o
> diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c
> new file mode 100644
> index 0000000..a4fa8ba
> --- /dev/null
> +++ b/drivers/net/can/usb/mcba_usb.c
> @@ -0,0 +1,1210 @@
> +/* SocketCAN driver for Microchip CAN BUS Analyzer Tool
> + *
> + * Copyright (C) 2016 Mobica Limited
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published
> + * by the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program.
> + *
> + * This driver is inspired by the 4.6.2 version of net/can/usb/usb_8dev.c
> + */
> +
> +#include <linux/usb.h>
> +#include <linux/can/dev.h>
> +
> +/* vendor and product id */
> +#define MCBA_MODULE_NAME         "mcba_usb"
> +#define MCBA_VENDOR_ID           0x04d8
> +#define MCBA_PRODUCT_ID          0x0a30
> +
> +/* driver constants */
> +#define MCBA_MAX_RX_URBS         20
> +#define MCBA_MAX_TX_URBS         20
> +#define MCBA_CTX_FREE            MCBA_MAX_TX_URBS
> +
> +/* RX buffer must be bigger than msg size since at the
> + * beggining USB messages are stacked.
> + */
> +#define MCBA_USB_RX_BUFF_SIZE    64
> +#define MCBA_USB_TX_BUFF_SIZE    (sizeof(struct mcba_usb_msg))
> +
> +/* MCBA endpoint numbers */
> +#define MCBA_USB_EP_IN           1
> +#define MCBA_USB_EP_OUT          1
> +
> +/* Not required by driver itself as CANBUS is USB based
> + * Used internally by candev for bitrate calculation
> + */
> +#define MCBA_CAN_CLOCK           40000000
> +
> +/* Microchip command id */
> +#define MBCA_CMD_RECEIVE_MESSAGE                0xE3
> +#define MBCA_CMD_I_AM_ALIVE_FROM_CAN            0xF5
> +#define MBCA_CMD_I_AM_ALIVE_FROM_USB            0xF7
> +#define MBCA_CMD_CHANGE_BIT_RATE                0xA1
> +#define MBCA_CMD_TRANSMIT_MESSAGE_EV            0xA3
> +#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE   0xA8
> +#define MBCA_CMD_READ_FW_VERSION                0xA9
> +#define MBCA_CMD_NOTHING_TO_SEND                0xFF
> +#define MBCA_CMD_TRANSMIT_MESSAGE_RSP           0xE2
> +
> +/* debug module parameter handling */
> +#define MCBA_PARAM_DEBUG_DISABLE    0
> +#define MCBA_PARAM_DEBUG_USB        1
> +#define MCBA_PARAM_DEBUG_CAN        2
> +#define MCBA_IS_USB_DEBUG()         (debug & MCBA_PARAM_DEBUG_USB)
> +#define MCBA_IS_CAN_DEBUG()         (debug & MCBA_PARAM_DEBUG_CAN)
> +
> +#define MCBA_VER_REQ_USB             1
> +#define MCBA_VER_REQ_CAN             2
> +
> +#define MCBA_CAN_S_SID0_SID2_MASK    0x7
> +#define MCBA_CAN_S_SID3_SID10_MASK   0x7F8
> +#define MCBA_CAN_S_SID3_SID10_SHIFT  3
> +
> +#define MCBA_CAN_EID0_EID7_MASK      0xff
> +#define MCBA_CAN_EID8_EID15_MASK     0xff00
> +#define MCBA_CAN_EID16_EID17_MASK    0x30000
> +#define MCBA_CAN_E_SID0_SID2_MASK    0x1c0000
> +#define MCBA_CAN_E_SID3_SID10_MASK   0x1fe00000
> +#define MCBA_CAN_EID8_EID15_SHIFT    8
> +#define MCBA_CAN_EID16_EID17_SHIFT   16
> +#define MCBA_CAN_E_SID0_SID2_SHIFT   18
> +#define MCBA_CAN_E_SID3_SID10_SHIFT  21
> +
> +#define MCBA_SIDL_SID0_SID2_MASK     0xe0
> +#define MCBA_SIDL_EXID_MASK          0x8
> +#define MCBA_SIDL_EID16_EID17_MASK   0x3
> +#define MCBA_SIDL_SID0_SID2_SHIFT    5
> +
> +#define MCBA_DLC_MASK                0xf
> +#define MCBA_DLC_RTR_MASK            0x40
> +
> +#define MCBA_CAN_RTR_MASK            0x40000000
> +#define MCBA_CAN_EXID_MASK           0x80000000
> +
> +#define MCBA_SET_S_SIDL(can_id)\
> +(((can_id) & MCBA_CAN_S_SID0_SID2_MASK) << MCBA_SIDL_SID0_SID2_SHIFT)
> +
> +#define MCBA_SET_E_SIDL(can_id)\
> +(((((can_id) & MCBA_CAN_E_SID0_SID2_MASK) >> MCBA_CAN_E_SID0_SID2_SHIFT)\
> +<< MCBA_SIDL_SID0_SID2_SHIFT) |\
> +(((can_id) & MCBA_CAN_EID16_EID17_MASK) >> MCBA_CAN_EID16_EID17_SHIFT) |\
> +MCBA_SIDL_EXID_MASK)
> +
> +#define MCBA_SET_S_SIDH(can_id)\
> +(((can_id) & MCBA_CAN_S_SID3_SID10_MASK) >> MCBA_CAN_S_SID3_SID10_SHIFT)
> +
> +#define MCBA_SET_E_SIDH(can_id)\
> +(((can_id) & MCBA_CAN_E_SID3_SID10_MASK) >> MCBA_CAN_E_SID3_SID10_SHIFT)
> +
> +#define MCBA_SET_EIDL(can_id)\
> +((can_id) & MCBA_CAN_EID0_EID7_MASK)
> +
> +#define MCBA_SET_EIDH(can_id)\
> +(((can_id) & MCBA_CAN_EID8_EID15_MASK) >> MCBA_CAN_EID8_EID15_SHIFT)
> +
> +#define MCBA_CAN_GET_SID(usb_msg)\
> +((((usb_msg)->sidl & MCBA_SIDL_SID0_SID2_MASK) >> MCBA_SIDL_SID0_SID2_SHIFT) |\
> +((usb_msg)->sidh << MCBA_CAN_S_SID3_SID10_SHIFT))
> +
> +#define MCBA_CAN_GET_EID(usb_msg)\
> +((((usb_msg)->sidh << MCBA_CAN_E_SID3_SID10_SHIFT) |\
> +(((usb_msg)->sidl & MCBA_SIDL_SID0_SID2_MASK) >> MCBA_SIDL_SID0_SID2_SHIFT) \
> +<< MCBA_CAN_E_SID0_SID2_SHIFT) |\
> +(((usb_msg)->sidl & MCBA_SIDL_EID16_EID17_MASK) \
> +<< MCBA_CAN_EID16_EID17_SHIFT) |\
> +((usb_msg)->eidh << MCBA_CAN_EID8_EID15_SHIFT) |\
> +(usb_msg)->eidl |\
> +MCBA_CAN_EXID_MASK)
> +
> +#define MCBA_RX_IS_EXID(usb_msg)    ((usb_msg)->sidl & MCBA_SIDL_EXID_MASK)
> +#define MCBA_RX_IS_RTR(usb_msg)     ((usb_msg)->dlc & MCBA_DLC_RTR_MASK)
> +#define MCBA_TX_IS_EXID(can_frame)  ((can_frame)->can_id & MCBA_CAN_EXID_MASK)
> +#define MCBA_TX_IS_RTR(can_frame)   ((can_frame)->can_id & MCBA_CAN_RTR_MASK)
> +
> +struct mcba_usb_ctx {
> +       struct mcba_priv *priv;
> +       u32 ndx;
> +       u8 dlc;
> +       bool can;
> +};
> +
> +/* Structure to hold all of our device specific stuff */
> +struct mcba_priv {
> +       struct can_priv can; /* must be the first member */
> +       struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
> +       struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
> +
> +       struct usb_device *udev;
> +       struct net_device *netdev;
> +       struct usb_anchor tx_submitted;
> +       struct usb_anchor rx_submitted;
> +       struct can_berr_counter bec;
> +       u8 termination_state;
> +       bool usb_ka_first_pass;
> +       bool can_ka_first_pass;
> +};
> +
> +/* command frame */
> +struct __packed mcba_usb_msg_can {
> +       u8 cmd_id;
> +       u8 eidh;
> +       u8 eidl;
> +       u8 sidh;
> +       u8 sidl;
> +       u8 dlc;
> +       u8 data[8];
> +       u8 timestamp[4];
> +       u8 checksum;
> +};
> +
> +/* command frame */
> +struct __packed mcba_usb_msg {
> +       u8 cmd_id;
> +       u8 unused[18];
> +};
> +
> +struct __packed mcba_usb_msg_ka_usb {
> +       u8 cmd_id;
> +       u8 termination_state;
> +       u8 soft_ver_major;
> +       u8 soft_ver_minor;
> +       u8 unused[15];
> +};
> +
> +struct __packed mcba_usb_msg_ka_can {
> +       u8 cmd_id;
> +       u8 tx_err_cnt;
> +       u8 rx_err_cnt;
> +       u8 rx_buff_ovfl;
> +       u8 tx_bus_off;
> +       u8 can_bitrate_hi;
> +       u8 can_bitrate_lo;
> +       u8 rx_lost_lo;
> +       u8 rx_lost_hi;
> +       u8 can_stat;
> +       u8 soft_ver_major;
> +       u8 soft_ver_minor;
> +       u8 debug_mode;
> +       u8 test_complete;
> +       u8 test_result;
> +       u8 unused[4];
> +};
> +
> +struct __packed mcba_usb_msg_change_bitrate {
> +       u8 cmd_id;
> +       u8 bitrate_hi;
> +       u8 bitrate_lo;
> +       u8 unused[16];
> +};
> +
> +struct __packed mcba_usb_msg_terminaton {
> +       u8 cmd_id;
> +       u8 termination;
> +       u8 unused[17];
> +};
> +
> +struct __packed mcba_usb_msg_fw_ver {
> +       u8 cmd_id;
> +       u8 pic;
> +       u8 unused[17];
> +};
> +
> +struct bitrate_settings {
> +       struct can_bittiming bt;
> +       u16 kbps;
> +};
> +
> +/* Required by can-dev but not for the sake of driver as CANBUS is USB based */
> +static const struct can_bittiming_const mcba_bittiming_const = {
> +       .name = "mcba_usb",
> +       .tseg1_min = 1,
> +       .tseg1_max = 8,
> +       .tseg2_min = 1,
> +       .tseg2_max = 8,
> +       .sjw_max = 4,
> +       .brp_min = 2,
> +       .brp_max = 128,
> +       .brp_inc = 2,
> +};
> +
> +/* predefined values hardcoded in device's firmware */
> +static const struct bitrate_settings br_settings[] = {
> +       {
> +               .bt = {
> +                       .bitrate = 19940,
> +                       .sample_point = 700,
> +                       .tq = 2500,
> +                       .prop_seg = 5,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 6,
> +                       .sjw = 1,
> +                       .brp = 100,
> +               },
> +               .kbps = 20
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 33333,
> +                       .sample_point = 680,
> +                       .tq = 1200,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 8,
> +                       .sjw = 1,
> +                       .brp = 48,
> +               },
> +               .kbps = 33
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 50000,
> +                       .sample_point = 800,
> +                       .tq = 1000,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 7,
> +                       .phase_seg2 = 4,
> +                       .sjw = 1,
> +                       .brp = 40,
> +               },
> +               .kbps = 50
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 80000,
> +                       .sample_point = 680,
> +                       .tq = 500,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 8,
> +                       .sjw = 1,
> +                       .brp = 20,
> +               },
> +               .kbps = 80
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 83333,
> +                       .sample_point = 708,
> +                       .tq = 500,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 7,
> +                       .sjw = 1,
> +                       .brp = 20,
> +               },
> +               .kbps = 83
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 100000,
> +                       .sample_point = 700,
> +                       .tq = 1000,
> +                       .prop_seg = 1,
> +                       .phase_seg1 = 5,
> +                       .phase_seg2 = 3,
> +                       .sjw = 1,
> +                       .brp = 40,
> +               },
> +               .kbps = 100
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 125000,
> +                       .sample_point = 600,
> +                       .tq = 400,
> +                       .prop_seg = 3,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 8,
> +                       .sjw = 1,
> +                       .brp = 16,
> +               },
> +               .kbps = 125
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 150375,
> +                       .sample_point = 789,
> +                       .tq = 350,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 6,
> +                       .phase_seg2 = 4,
> +                       .sjw = 1,
> +                       .brp = 14,
> +               },
> +               .kbps = 150
> +       },
> +
> +       {
> +               .bt = {
> +                       .bitrate = 175438,
> +                       .sample_point = 789,
> +                       .tq = 300,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 6,
> +                       .phase_seg2 = 4,
> +                       .sjw = 1,
> +                       .brp = 12,
> +               },
> +               .kbps = 175
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 200000,
> +                       .sample_point = 680,
> +                       .tq = 200,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 8,
> +                       .sjw = 1,
> +                       .brp = 8,
> +               },
> +               .kbps = 200
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 227272,
> +                       .sample_point = 772,
> +                       .tq = 200,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 5,
> +                       .sjw = 1,
> +                       .brp = 8,
> +               },
> +               .kbps = 225
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 250000,
> +                       .sample_point = 600,
> +                       .tq = 200,
> +                       .prop_seg = 3,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 8,
> +                       .sjw = 1,
> +                       .brp = 8,
> +               },
> +               .kbps = 250
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 277777,
> +                       .sample_point = 708,
> +                       .tq = 150,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 7,
> +                       .sjw = 1,
> +                       .brp = 6,
> +               },
> +               .kbps = 275
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 303030,
> +                       .sample_point = 772,
> +                       .tq = 150,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 5,
> +                       .sjw = 1,
> +                       .brp = 6,
> +               },
> +               .kbps = 300
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 500000,
> +                       .sample_point = 600,
> +                       .tq = 100,
> +                       .prop_seg = 3,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 8,
> +                       .sjw = 1,
> +                       .brp = 4,
> +               },
> +               .kbps = 500
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 625000,
> +                       .sample_point = 750,
> +                       .tq = 200,
> +                       .prop_seg = 1,
> +                       .phase_seg1 = 4,
> +                       .phase_seg2 = 2,
> +                       .sjw = 1,
> +                       .brp = 8,
> +               },
> +               .kbps = 625
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 800000,
> +                       .sample_point = 680,
> +                       .tq = 50,
> +                       .prop_seg = 8,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 8,
> +                       .sjw = 1,
> +                       .brp = 2,
> +               },
> +               .kbps = 800
> +       },
> +       {
> +               .bt = {
> +                       .bitrate = 1000000,
> +                       .sample_point = 600,
> +                       .tq = 50,
> +                       .prop_seg = 3,
> +                       .phase_seg1 = 8,
> +                       .phase_seg2 = 8,
> +                       .sjw = 1,
> +                       .brp = 2,
> +               },
> +               .kbps = 1000
> +       }
> +};
> +
> +static int debug;
> +module_param(debug, int, 0664);
> +MODULE_PARM_DESC(debug,
> +                "Binary flag to control device debug (keep alive) prints in dmesg. 0='Debug prints disabled' "
> +                __stringify(MCBA_PARAM_DEBUG_USB) "='PIC_USB debugs enabled' "
> +                __stringify(MCBA_PARAM_DEBUG_CAN) "='PIC_CAN debugs enabled'");
> +
> +static const struct usb_device_id mcba_usb_table[] = {
> +       { USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
> +       { } /* Terminating entry */
> +};
> +
> +MODULE_DEVICE_TABLE(usb, mcba_usb_table);
> +
> +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv,
> +                                struct mcba_usb_msg *usb_msg,
> +                                struct sk_buff *skb);
> +static void mcba_usb_xmit_cmd(struct mcba_priv *priv,
> +                             struct mcba_usb_msg *usb_msg);
> +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic);
> +static void mcba_usb_xmit_termination(struct mcba_priv *priv, u8 termination);
> +static inline void mcba_init_ctx(struct mcba_priv *priv);
> +
> +static ssize_t termination_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct net_device *netdev = to_net_dev(dev);
> +       struct mcba_priv *priv = netdev_priv(netdev);
> +
> +       return sprintf(buf, "%hhu\n", priv->termination_state);
> +}
> +
> +static ssize_t termination_store(struct device *dev,
> +                                struct device_attribute *attr,
> +                                const char *buf, size_t count)
> +{
> +       struct net_device *netdev = to_net_dev(dev);
> +       struct mcba_priv *priv = netdev_priv(netdev);
> +       int tmp = -1;
> +       int ret = -1;
> +
> +       ret = kstrtoint(buf, 10, &tmp);
> +
> +       if ((ret == 0) && ((tmp == 0) || (tmp == 1))) {
> +               priv->termination_state = tmp;
> +               mcba_usb_xmit_termination(priv, priv->termination_state);
> +       }
> +
> +       return count;
> +}
> +
> +static struct device_attribute termination_attr = {
> +       .attr = {
> +               .name = "termination",
> +               .mode = 0666 },
> +       .show   = termination_show,
> +       .store  = termination_store
> +};
> +
> +static void mcba_usb_process_can(struct mcba_priv *priv,
> +                                struct mcba_usb_msg_can *msg)
> +{
> +       struct can_frame *cf;
> +       struct sk_buff *skb;
> +       struct net_device_stats *stats = &priv->netdev->stats;
> +
> +       skb = alloc_can_skb(priv->netdev, &cf);
> +       if (!skb)
> +               return;
> +
> +       if (MCBA_RX_IS_EXID(msg))
> +               cf->can_id = MCBA_CAN_GET_EID(msg);
> +       else
> +               cf->can_id = MCBA_CAN_GET_SID(msg);
> +
> +       if (MCBA_RX_IS_RTR(msg))
> +               cf->can_id |= MCBA_CAN_RTR_MASK;
> +
> +       cf->can_dlc = msg->dlc & MCBA_DLC_MASK;
> +
> +       memcpy(cf->data, msg->data, cf->can_dlc);
> +
> +       stats->rx_packets++;
> +       stats->rx_bytes += cf->can_dlc;
> +       netif_rx(skb);
> +}
> +
> +static void mcba_usb_process_ka_usb(struct mcba_priv *priv,
> +                                   struct mcba_usb_msg_ka_usb *msg)
> +{
> +       if (unlikely(MCBA_IS_USB_DEBUG())) {
> +               netdev_info(priv->netdev,
> +                           "USB_KA: termination %hhu, ver_maj %hhu, soft_min %hhu\n",
> +                           msg->termination_state, msg->soft_ver_major,
> +                           msg->soft_ver_minor);
> +       }
> +
> +       if (unlikely(priv->usb_ka_first_pass)) {
> +               netdev_info(priv->netdev,
> +                           "PIC USB version %hhu.%hhu\n",
> +                           msg->soft_ver_major, msg->soft_ver_minor);
> +
> +               priv->usb_ka_first_pass = false;
> +       }
> +
> +       priv->termination_state = msg->termination_state;
> +}
> +
> +static void mcba_usb_process_ka_can(struct mcba_priv *priv,
> +                                   struct mcba_usb_msg_ka_can *msg)
> +{
> +       if (unlikely(MCBA_IS_CAN_DEBUG())) {
> +               netdev_info(priv->netdev,
> +                           "CAN_KA: tx_err_cnt %hhu, rx_err_cnt %hhu, rx_buff_ovfl %hhu, tx_bus_off %hhu, can_bitrate %hu, rx_lost %hu, can_stat %hhu, soft_ver %hhu.%hhu, debug_mode %hhu, test_complete %hhu, test_result %hhu\n",
> +                           msg->tx_err_cnt, msg->rx_err_cnt, msg->rx_buff_ovfl,
> +                           msg->tx_bus_off,
> +                           ((msg->can_bitrate_hi << 8) + msg->can_bitrate_lo),
> +                           ((msg->rx_lost_hi >> 8) + msg->rx_lost_lo),
> +                           msg->can_stat, msg->soft_ver_major,
> +                           msg->soft_ver_minor,
> +                           msg->debug_mode, msg->test_complete,
> +                           msg->test_result);
> +       }
> +
> +       if (unlikely(priv->can_ka_first_pass)) {
> +               netdev_info(priv->netdev,
> +                           "PIC CAN version %hhu.%hhu\n",
> +                           msg->soft_ver_major, msg->soft_ver_minor);
> +
> +               priv->can_ka_first_pass = false;
> +       }
> +
> +       priv->bec.txerr = msg->tx_err_cnt;
> +       priv->bec.rxerr = msg->rx_err_cnt;
> +}
> +
> +static void mcba_usb_process_rx(struct mcba_priv *priv,
> +                               struct mcba_usb_msg *msg)
> +{
> +       switch (msg->cmd_id) {
> +       case MBCA_CMD_I_AM_ALIVE_FROM_CAN:
> +               mcba_usb_process_ka_can(priv,
> +                                       (struct mcba_usb_msg_ka_can *)msg);
> +               break;
> +
> +       case MBCA_CMD_I_AM_ALIVE_FROM_USB:
> +               mcba_usb_process_ka_usb(priv,
> +                                       (struct mcba_usb_msg_ka_usb *)msg);
> +               break;
> +
> +       case MBCA_CMD_RECEIVE_MESSAGE:
> +               mcba_usb_process_can(priv, (struct mcba_usb_msg_can *)msg);
> +               break;
> +
> +       case MBCA_CMD_NOTHING_TO_SEND:
> +               /* Side effect of communication between PIC_USB and PIC_CAN.
> +                * PIC_CAN is telling us that it has nothing to send
> +                */
> +               break;
> +
> +       case MBCA_CMD_TRANSMIT_MESSAGE_RSP:
> +               /* Transmission response from the device containing timestamp */
> +               break;
> +
> +       default:
> +               netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)",
> +                           msg->cmd_id);
> +               break;
> +       }
> +}
> +
> +/* Callback for reading data from device
> + *
> + * Check urb status, call read function and resubmit urb read operation.
> + */
> +static void mcba_usb_read_bulk_callback(struct urb *urb)
> +{
> +       struct mcba_priv *priv = urb->context;
> +       struct net_device *netdev;
> +       int retval;
> +       int pos = 0;
> +
> +       netdev = priv->netdev;
> +
> +       if (!netif_device_present(netdev))
> +               return;
> +
> +       switch (urb->status) {
> +       case 0: /* success */
> +               break;
> +
> +       case -ENOENT:
> +       case -ESHUTDOWN:
> +               return;
> +
> +       default:
> +               netdev_info(netdev, "Rx URB aborted (%d)\n",
> +                           urb->status);
> +
> +               goto resubmit_urb;
> +       }
> +
> +       while (pos < urb->actual_length) {
> +               struct mcba_usb_msg *msg;
> +
> +               if (pos + sizeof(struct mcba_usb_msg) > urb->actual_length) {
> +                       netdev_err(priv->netdev, "format error\n");
> +                       break;
> +               }
> +
> +               msg = (struct mcba_usb_msg *)(urb->transfer_buffer + pos);
> +               mcba_usb_process_rx(priv, msg);
> +
> +               pos += sizeof(struct mcba_usb_msg);
> +       }
> +
> +resubmit_urb:
> +
> +       usb_fill_bulk_urb(urb, priv->udev,
> +                         usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_OUT),
> +                         urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE,
> +                         mcba_usb_read_bulk_callback, priv);
> +
> +       retval = usb_submit_urb(urb, GFP_ATOMIC);
> +
> +       if (retval == -ENODEV)
> +               netif_device_detach(netdev);
> +       else if (retval)
> +               netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
> +                          retval);
> +}
> +
> +/* Start USB device */
> +static int mcba_usb_start(struct mcba_priv *priv)
> +{
> +       struct net_device *netdev = priv->netdev;
> +       int err, i;
> +
> +       for (i = 0; i < MCBA_MAX_RX_URBS; i++) {
> +               struct urb *urb = NULL;
> +               u8 *buf;
> +
> +               /* create a URB, and a buffer for it */
> +               urb = usb_alloc_urb(0, GFP_KERNEL);
> +               if (!urb) {
> +                       netdev_err(netdev, "No memory left for URBs\n");
> +                       err = -ENOMEM;
> +                       break;
> +               }
> +
> +               buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
> +                                        GFP_KERNEL,
> +                                        &urb->transfer_dma);
> +               if (!buf) {
> +                       netdev_err(netdev, "No memory left for USB buffer\n");
> +                       usb_free_urb(urb);
> +                       err = -ENOMEM;
> +                       break;
> +               }
> +
> +               usb_fill_bulk_urb(urb, priv->udev,
> +                                 usb_rcvbulkpipe(priv->udev,
> +                                                 MCBA_USB_EP_IN),
> +                                 buf, MCBA_USB_RX_BUFF_SIZE,
> +                                 mcba_usb_read_bulk_callback, priv);
> +               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +               usb_anchor_urb(urb, &priv->rx_submitted);
> +
> +               err = usb_submit_urb(urb, GFP_KERNEL);
> +               if (err) {
> +                       usb_unanchor_urb(urb);
> +                       usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
> +                                         buf, urb->transfer_dma);
> +                       usb_free_urb(urb);
> +                       break;
> +               }
> +
> +               /* Drop reference, USB core will take care of freeing it */
> +               usb_free_urb(urb);
> +       }
> +
> +       /* Did we submit any URBs */
> +       if (i == 0) {
> +               netdev_warn(netdev, "couldn't setup read URBs\n");
> +               return err;
> +       }
> +
> +       /* Warn if we've couldn't transmit all the URBs */
> +       if (i < MCBA_MAX_RX_URBS)
> +               netdev_warn(netdev, "rx performance may be slow\n");
> +
> +       priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +       mcba_init_ctx(priv);
> +       mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB);
> +       mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN);
> +
> +       return err;
> +}
> +
> +static inline void mcba_init_ctx(struct mcba_priv *priv)
> +{
> +       int i = 0;
> +
> +       for (i = 0; i < MCBA_MAX_TX_URBS; i++)
> +               priv->tx_context[i].ndx = MCBA_CTX_FREE;
> +}
> +
> +static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv)
> +{
> +       int i = 0;
> +       struct mcba_usb_ctx *ctx = 0;
> +
> +       for (i = 0; i < MCBA_MAX_TX_URBS; i++) {
> +               if (priv->tx_context[i].ndx == MCBA_CTX_FREE) {
> +                       ctx = &priv->tx_context[i];
> +                       ctx->ndx = i;
> +                       ctx->priv = priv;
> +                       break;
> +               }
> +       }
> +
> +       return ctx;
> +}
> +
> +static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx)
> +{
> +       ctx->ndx = MCBA_CTX_FREE;
> +       ctx->priv = 0;
> +       ctx->dlc = 0;
> +       ctx->can = false;
> +}
> +
> +static void mcba_usb_write_bulk_callback(struct urb *urb)
> +{
> +       struct mcba_usb_ctx *ctx = urb->context;
> +       struct net_device *netdev;
> +
> +       WARN_ON(!ctx);
> +
> +       netdev = ctx->priv->netdev;
> +
> +       if (ctx->can) {
> +               if (!netif_device_present(netdev))
> +                       return;
> +
> +               netdev->stats.tx_packets++;
> +               netdev->stats.tx_bytes += ctx->dlc;
> +
> +               can_get_echo_skb(netdev, ctx->ndx);
> +
> +               netif_wake_queue(netdev);
> +       }
> +
> +       /* free up our allocated buffer */
> +       usb_free_coherent(urb->dev, urb->transfer_buffer_length,
> +                         urb->transfer_buffer, urb->transfer_dma);
> +
> +       if (urb->status)
> +               netdev_info(netdev, "Tx URB aborted (%d)\n",
> +                           urb->status);
> +
> +       /* Release context */
> +       mcba_usb_free_ctx(ctx);
> +}
> +
> +/* Send data to device */
> +static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
> +                                      struct net_device *netdev)
> +{
> +       struct mcba_priv *priv = netdev_priv(netdev);
> +       struct can_frame *cf = (struct can_frame *)skb->data;
> +       struct mcba_usb_msg_can usb_msg;
> +
> +       usb_msg.cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV;
> +       memcpy(usb_msg.data, cf->data, sizeof(usb_msg.data));
> +
> +       if (MCBA_TX_IS_EXID(cf)) {
> +               usb_msg.sidl = MCBA_SET_E_SIDL(cf->can_id);
> +               usb_msg.sidh = MCBA_SET_E_SIDH(cf->can_id);
> +               usb_msg.eidl = MCBA_SET_EIDL(cf->can_id);
> +               usb_msg.eidh = MCBA_SET_EIDH(cf->can_id);
> +       } else {
> +               usb_msg.sidl = MCBA_SET_S_SIDL(cf->can_id);
> +               usb_msg.sidh = MCBA_SET_S_SIDH(cf->can_id);
> +               usb_msg.eidl = 0;
> +               usb_msg.eidh = 0;
> +       }
> +
> +       usb_msg.dlc = cf->can_dlc;
> +
> +       if (MCBA_TX_IS_RTR(cf))
> +               usb_msg.dlc |= MCBA_DLC_RTR_MASK;
> +
> +       return mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, skb);
> +}
> +
> +/* Send data to device */
> +static void mcba_usb_xmit_cmd(struct mcba_priv *priv,
> +                             struct mcba_usb_msg *usb_msg)
> +{
> +       mcba_usb_xmit(priv, usb_msg, 0);
> +}
> +
> +/* Send data to device */
> +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv,
> +                                struct mcba_usb_msg *usb_msg,
> +                                struct sk_buff *skb)
> +{
> +       struct net_device_stats *stats = &priv->netdev->stats;
> +       struct mcba_usb_ctx *ctx = 0;
> +       struct urb *urb;
> +       u8 *buf;
> +       int err;
> +
> +       ctx = mcba_usb_get_free_ctx(priv);
> +       if (!ctx) {
> +               /* Slow down tx path */
> +               netif_stop_queue(priv->netdev);
> +
> +               return NETDEV_TX_BUSY;
> +       }
> +
> +       if (skb) {
> +               ctx->dlc = ((struct mcba_usb_msg_can *)usb_msg)->dlc
> +                               & MCBA_DLC_MASK;
> +               can_put_echo_skb(skb, priv->netdev, ctx->ndx);
> +               ctx->can = true;
> +       } else {
> +               ctx->can = false;
> +       }
> +
> +       /* create a URB, and a buffer for it, and copy the data to the URB */
> +       urb = usb_alloc_urb(0, GFP_ATOMIC);
> +       if (!urb) {
> +               netdev_err(priv->netdev, "No memory left for URBs\n");
> +               goto nomem;
> +       }
> +
> +       buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, GFP_ATOMIC,
> +                                &urb->transfer_dma);
> +       if (!buf) {
> +               netdev_err(priv->netdev, "No memory left for USB buffer\n");
> +               goto nomembuf;
> +       }
> +
> +       memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE);
> +
> +       usb_fill_bulk_urb(urb, priv->udev,
> +                         usb_sndbulkpipe(priv->udev, MCBA_USB_EP_OUT), buf,
> +                         MCBA_USB_TX_BUFF_SIZE, mcba_usb_write_bulk_callback,
> +                         ctx);
> +
> +       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +       usb_anchor_urb(urb, &priv->tx_submitted);
> +
> +       err = usb_submit_urb(urb, GFP_ATOMIC);
> +       if (unlikely(err))
> +               goto failed;
> +
> +       /* Release our reference to this URB, the USB core will eventually free
> +        * it entirely.
> +        */
> +       usb_free_urb(urb);
> +
> +       return NETDEV_TX_OK;
> +
> +failed:
> +       usb_unanchor_urb(urb);
> +       usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf,
> +                         urb->transfer_dma);
> +
> +       if (err == -ENODEV)
> +               netif_device_detach(priv->netdev);
> +       else
> +               netdev_warn(priv->netdev, "failed tx_urb %d\n", err);
> +
> +nomembuf:
> +       usb_free_urb(urb);
> +
> +nomem:
> +       can_free_echo_skb(priv->netdev, ctx->ndx);
> +       dev_kfree_skb(skb);
> +       stats->tx_dropped++;
> +
> +       return NETDEV_TX_OK;
> +}
> +
> +static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16 bitrate)
> +{
> +       struct mcba_usb_msg_change_bitrate usb_msg;
> +
> +       usb_msg.cmd_id =  MBCA_CMD_CHANGE_BIT_RATE;
> +       usb_msg.bitrate_hi = (0xff00 & bitrate) >> 8;
> +       usb_msg.bitrate_lo = (0xff & bitrate);
> +
> +       mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
> +}
> +
> +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic)
> +{
> +       struct mcba_usb_msg_fw_ver usb_msg;
> +
> +       usb_msg.cmd_id = MBCA_CMD_READ_FW_VERSION;
> +       usb_msg.pic = pic;
> +
> +       mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
> +}
> +
> +static void mcba_usb_xmit_termination(struct mcba_priv *priv, u8 termination)
> +{
> +       struct mcba_usb_msg_terminaton usb_msg;
> +
> +       usb_msg.cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE;
> +       usb_msg.termination = termination;
> +
> +       mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
> +}
> +
> +/* Open USB device */
> +static int mcba_usb_open(struct net_device *netdev)
> +{
> +       int err;
> +
> +       /* common open */
> +       err = open_candev(netdev);
> +       if (err)
> +               return err;
> +
> +       can_led_event(netdev, CAN_LED_EVENT_OPEN);
> +
> +       netif_start_queue(netdev);
> +
> +       return 0;
> +}
> +
> +static void mcba_urb_unlink(struct mcba_priv *priv)
> +{
> +       usb_kill_anchored_urbs(&priv->rx_submitted);
> +       usb_kill_anchored_urbs(&priv->tx_submitted);
> +}
> +
> +/* Close USB device */
> +static int mcba_usb_close(struct net_device *netdev)
> +{
> +       struct mcba_priv *priv = netdev_priv(netdev);
> +
> +       priv->can.state = CAN_STATE_STOPPED;
> +
> +       netif_stop_queue(netdev);
> +
> +       /* Stop polling */
> +       mcba_urb_unlink(priv);
> +
> +       close_candev(netdev);
> +
> +       can_led_event(netdev, CAN_LED_EVENT_STOP);
> +
> +       return 0;
> +}
> +
> +/* Set network device mode
> + *
> + * Maybe we should leave this function empty, because the device
> + * set mode variable with open command.
> + */
> +static int mcba_net_set_mode(struct net_device *netdev, enum can_mode mode)
> +{
> +       return 0;
> +}
> +
> +static int mcba_net_get_berr_counter(const struct net_device *netdev,
> +                                    struct can_berr_counter *bec)
> +{
> +       struct mcba_priv *priv = netdev_priv(netdev);
> +
> +       bec->txerr = priv->bec.txerr;
> +       bec->rxerr = priv->bec.rxerr;
> +
> +       return 0;
> +}
> +
> +static const struct net_device_ops mcba_netdev_ops = {
> +       .ndo_open = mcba_usb_open,
> +       .ndo_stop = mcba_usb_close,
> +       .ndo_start_xmit = mcba_usb_start_xmit
> +};
> +
> +/* Microchip CANBUS has hardcoded bittiming values by default.
> + * This function sends request via USB to change the speed and align bittiming
> + * values for presentation purposes only
> + */
> +static int mcba_net_set_bittiming(struct net_device *netdev)
> +{
> +       u8 i;
> +       struct mcba_priv *priv = netdev_priv(netdev);
> +       struct can_bittiming *bt = &priv->can.bittiming;
> +       const struct bitrate_settings *settings = 0;
> +       const u8 setting_cnt = sizeof(br_settings) /
> +                              sizeof(struct bitrate_settings);
> +
> +       for (i = 0; i < setting_cnt; ++i)
> +               if (br_settings[i].bt.bitrate == bt->bitrate)
> +                       settings = &br_settings[i];
> +
> +       if (settings) {
> +               memcpy(bt, &settings->bt, sizeof(struct can_bittiming));
> +
> +               /* recalculate bitrate as it may be different than default */
> +               bt->bitrate = 1000000000 / ((bt->sjw + bt->prop_seg +
> +                                           bt->phase_seg1 + bt->phase_seg2) *
> +                                           bt->tq);
> +
> +               mcba_usb_xmit_change_bitrate(priv, settings->kbps);
> +       } else {
> +               netdev_err(netdev, "Unsupported bittrate (%u). Use one of: 20000, 33333, 50000, 80000, 83333, 100000, 125000, 150000, 175000, 200000, 225000, 250000, 275000, 300000, 500000, 625000, 800000, 1000000\n",
> +                          bt->bitrate);
> +
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int mcba_usb_probe(struct usb_interface *intf,
> +                         const struct usb_device_id *id)
> +{
> +       struct net_device *netdev;
> +       struct mcba_priv *priv;
> +       int err = -ENOMEM;
> +       struct usb_device *usbdev = interface_to_usbdev(intf);
> +
> +       dev_info(&intf->dev, "Microchip CAN BUS analizer connected\n");
> +
> +       netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS);
> +       if (!netdev) {
> +               dev_err(&intf->dev, "Couldn't alloc candev\n");
> +               return -ENOMEM;
> +       }
> +
> +       priv = netdev_priv(netdev);
> +
> +       priv->udev = usbdev;
> +       priv->netdev = netdev;
> +       priv->usb_ka_first_pass = true;
> +       priv->can_ka_first_pass = true;
> +
> +       init_usb_anchor(&priv->rx_submitted);
> +       init_usb_anchor(&priv->tx_submitted);
> +
> +       usb_set_intfdata(intf, priv);
> +
> +       err = mcba_usb_start(priv);
> +       if (err) {
> +               if (err == -ENODEV)
> +                       netif_device_detach(priv->netdev);
> +
> +               netdev_warn(netdev, "couldn't start device: %d\n", err);
> +
> +               goto cleanup_candev;
> +       }
> +
> +       /* Init CAN device */
> +       priv->can.state = CAN_STATE_STOPPED;
> +       priv->can.clock.freq = MCBA_CAN_CLOCK;
> +       priv->can.bittiming_const = &mcba_bittiming_const;
> +       priv->can.do_set_mode = mcba_net_set_mode;
> +       priv->can.do_get_berr_counter = mcba_net_get_berr_counter;
> +       priv->can.do_set_bittiming = mcba_net_set_bittiming;
> +       priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
> +                       CAN_CTRLMODE_LISTENONLY |
> +                       CAN_CTRLMODE_ONE_SHOT;
> +
> +       netdev->netdev_ops = &mcba_netdev_ops;
> +
> +       netdev->flags |= IFF_ECHO; /* we support local echo */
> +
> +       SET_NETDEV_DEV(netdev, &intf->dev);
> +
> +       err = register_candev(netdev);
> +       if (err) {
> +               netdev_err(netdev,
> +                          "couldn't register CAN device: %d\n", err);
> +               goto cleanup_candev;
> +       }
> +
> +       err = device_create_file(&netdev->dev, &termination_attr);
> +       if (err)
> +               goto cleanup_unregister_candev;
> +
> +       return err;
> +
> +cleanup_unregister_candev:
> +       unregister_candev(netdev);
> +
> +cleanup_candev:
> +       free_candev(netdev);
> +
> +       return err;
> +}
> +
> +/* Called by the usb core when driver is unloaded or device is removed */
> +static void mcba_usb_disconnect(struct usb_interface *intf)
> +{
> +       struct mcba_priv *priv = usb_get_intfdata(intf);
> +
> +       device_remove_file(&priv->netdev->dev, &termination_attr);
> +
> +       usb_set_intfdata(intf, NULL);
> +
> +       if (priv) {
> +               netdev_info(priv->netdev, "device disconnected\n");
> +
> +               unregister_candev(priv->netdev);
> +               free_candev(priv->netdev);
> +
> +               mcba_urb_unlink(priv);
> +       }
> +}
> +
> +static struct usb_driver mcba_usb_driver = {
> +       .name =         MCBA_MODULE_NAME,
> +       .probe =        mcba_usb_probe,
> +       .disconnect =   mcba_usb_disconnect,
> +       .id_table =     mcba_usb_table,
> +};
> +
> +module_usb_driver(mcba_usb_driver);
> +
> +MODULE_AUTHOR("Remigiusz Kołłątaj <remigiusz.kollataj@mobica.com>");
> +MODULE_DESCRIPTION("SocketCAN driver for Microchip CAN BUS Analyzer Tool");
> +MODULE_LICENSE("GPL v2");
> --
> 2.10.1
>

^ permalink raw reply

* Re: Troubleshooting bus errors, bus turns off during canplayer playback
From: Tom Evans @ 2016-12-20  5:01 UTC (permalink / raw)
  To: Hans L, linux-can
In-Reply-To: <CAFoRp-PSPtdenVSihp+Ar-1xfsOQd1M6rrUN6kgMOGd2egCcoA@mail.gmail.com>

On 20/12/16 15:53, Hans L wrote:
> I'm a developer writing firmware for a J1939 device and I have some
> logs recorded from vehicles that I want to use as test data, playing
> these on a "bench" setup.
>
> I am using a Beaglebone black running debian, with a PEAK PCAN-USB
> adapter to play these logs on my test bus.
>
> When I attempt to play a log that has pretty high bus utilization, it
> just quits after sending few frames with "No buffer space available".

That's an old problem that everyone runs into eventually.

It has come up before on this list.

Here's something I posted in March 2015:

====

Sockets can block or return ENOBUFS. Ethernet blocks before it returns ENOBUFS 
like you'd expect. With CAN it does the opposite "out of the box" and needs to 
be fixed.

http://socket-can.996257.n3.nabble.com/Solving-ENOBUFS-returned-by-write-td2886.html

     With Ethernet, the transmit queue length is 1000 (which would
     return ENOBUF) but before that happens it hits SO_SNDBUF,
     which may be 108544, which is the total Data plus SKB, and
     with an SKB size of about 200 that means it blocks at about
     500 before it ENOBUFs at 1000.

     With CAN, it would block at 500, but it ENOBUFs at 10 first with
     the default queue depth!

I do the following to get a 256-deep queue that blocks before it overflows:

     /bin/echo 256 > /sys/class/net/can0/tx_queue_len
     /bin/echo 256 > /sys/class/net/can1/tx_queue_len

     int sndbuf = (250 + 8) * 256;
     socklen_t socklen = sizeof(sndbuf);
     /* Minimum socket buffer to try and get it blocking */
     rc = setsockopt(pSkt->skt, SOL_SOCKET, SO_SNDBUF,
                     &sndbuf, sizeof(sndbuf));

You might also like to read:

http://rtime.felk.cvut.cz/can/socketcan-qdisc-final.pdf

     SocketCAN and queueing disciplines:
     Final Report
     M. Sojka, R. Lisov\x13y, P. P\x13\x10\x14sa
     Czech Technical University in Prague
     July 20, 2012
     Version 1.2

Tom


^ permalink raw reply

* Re: Troubleshooting bus errors, bus turns off during canplayer playback
From: Brian Silverman @ 2016-12-20  5:23 UTC (permalink / raw)
  To: Hans L; +Cc: linux-can
In-Reply-To: <CAFoRp-PSPtdenVSihp+Ar-1xfsOQd1M6rrUN6kgMOGd2egCcoA@mail.gmail.com>

Are you sending messages from the log file with the same source
address as something else currently on the bus is sending from? I get
those same symptoms when I do that (doing the same thing too). I'm
pretty sure the problem is both nodes think they won the arbitration
because they're using the same address, but then the first to try
sending a recessive data bit when the other one's sending a dominant
one gets a bit error. The first node which detected the error then
sends an error frame so all nodes on the bus ignore the frame and see
an error.

I'm using a PEAK PCAN-miniPCIe card with 3.14.43-rt42, and I have a
"berr-counter tx NNN rx NNN" in the `ip -d -s link show` output. I
don't recall whether I've ever looked at those when two nodes are
sending with the same address to see if they increment, but I can
check in a few days after I'm done travelling. For reference, this is
what that output looks like on a random box of mine (I think it has no
CAN wires attached):
brian@box:~$ sudo ip -d -s link show can0
3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state
UNKNOWN mode DEFAULT group default qlen 10
    link/can  promiscuity 0
    can <TRIPLE-SAMPLING> state ERROR-PASSIVE (berr-counter tx 0 rx
135) restart-ms 1000
        bitrate 250000 sample-point 0.875
        tq 250 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
        sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
        clock 8000000
        re-started bus-errors arbit-lost error-warn error-pass bus-off
        0          0          -688250473 1          1          0
    RX: bytes  packets  errors  dropped overrun mcast
    28853734600 3606716825 0       0       0       0
    TX: bytes  packets  errors  dropped carrier collsns
    0          0        3606716823 0       0       0

Interestingly, that appears to be without berr-reporting on. When I
explicitly pass "berr-reporting on" in the `ip link set can0 up ...`
command, BERR-REPORTING shows up next to TRIPLE-SAMPLING as expected.
I'll have to check if that has any noticeable effect on the numbers
reported. I too would appreciate a pointer to more detailed
documentation on what exactly that option does.

^ permalink raw reply

* Troubleshooting bus errors, bus turns off during canplayer playback
From: Hans L @ 2016-12-20  4:53 UTC (permalink / raw)
  To: linux-can

I'm a developer writing firmware for a J1939 device and I have some
logs recorded from vehicles that I want to use as test data, playing
these on a "bench" setup.

I am using a Beaglebone black running debian, with a PEAK PCAN-USB
adapter to play these logs on my test bus.

When I attempt to play a log that has pretty high bus utilization, it
just quits after sending few frames with "No buffer space available".
I found that default txqueuelen was 10 and increasing it to 1000 helps
get rid of the buffer space issues, but then the canplayer process
still just stops transmitting after 300 frames or so.
I guess the bus is encountering some sort of error, and turning off,
so I set restart-ms 1 to have minimal downtime.

With these settings it seems that canplayer is able to get through the
whole log and transmit all frames, but I don't understand why the bus
has to restart so much in the first place, or the cause of these
errors that regularly kill the bus?

Also many options that I tried such as "berr-reporting on" result in
"RTNETLINK answers: Operation not supported".
And where are the descriptions of what these settings do? I search
through the manual but berr-reporting is only mentioned once in the
options list, but no explanation(same for most other settings)
https://www.kernel.org/doc/Documentation/networking/can.txt

Below is some system info:

debian@beaglebone:~$ uname -a
Linux beaglebone 3.8.13-bone79 #1 SMP Tue Oct 13 20:44:55 UTC 2015
armv7l GNU/Linux

debian@beaglebone:~$ sudo modinfo peak_usb
filename:
/lib/modules/3.8.13-bone79/kernel/drivers/net/can/usb/peak_usb/peak_usb.ko
license:        GPL v2
description:    CAN driver for PEAK-System USB adapters
author:         Stephane Grosjean <s.grosjean@peak-system.com>
srcversion:     E230D7B8207864FEABBECC6
alias:          usb:v0C72p000Dd*dc*dsc*dp*ic*isc*ip*in*
alias:          usb:v0C72p000Cd*dc*dsc*dp*ic*isc*ip*in*
depends:        can-dev
intree:         Y
vermagic:       3.8.13-bone79 SMP mod_unload modversions ARMv7 thumb2 p2v8

debian@beaglebone:~$ ip -d -s link show can0
5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP
mode DEFAULT qlen 1000
    link/can
    can state ERROR-PASSIVE restart-ms 1
    bitrate 250000 sample-point 0.875
    tq 250 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
    pcan_usb: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    18         0          0          36         41         20
    RX: bytes  packets  errors  dropped overrun mcast
    10167      2709     0       0       0       0
    TX: bytes  packets  errors  dropped carrier collsns
    1663258    207911   0       46      0       0

^ permalink raw reply

* Re: [PATCH 8/8] Makefile: drop -D__CHECK_ENDIAN__ from cflags
From: Luca Coelho @ 2016-12-19  9:08 UTC (permalink / raw)
  To: Michael S. Tsirkin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Emmanuel Grumbach, Stanislaw Gruszka, Gustavo Padovan,
	Arend van Spriel, devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	Jakub Kicinski, Stefan Schmidt,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	wil6210-A+ZNKFmMK5xy9aJCnZT0Uw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Chris Snook,
	Wolfgang Grandegger, Jay Cliburn,
	linux-wpan-u79uwXL29TY76Z2rM5mHXA, Johan Hedberg, Johannes Berg,
	Intel Linux Wireless, Alexander Aring, Marcel Holtmann,
	Hante Meuleman, linux-can-u79uwXL29TY76Z2rM5mHXA,
	Marc Kleine-Budde, Matthias Brugger, Kall
In-Reply-To: <1481778865-27667-9-git-send-email-mst-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

On Thu, 2016-12-15 at 07:15 +0200, Michael S. Tsirkin wrote:
> That's the default now, no need for makefiles to set it.
> 
> Signed-off-by: Michael S. Tsirkin <mst-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> ---
>  drivers/bluetooth/Makefile                                | 2 --
>  drivers/net/can/Makefile                                  | 1 -
>  drivers/net/ethernet/altera/Makefile                      | 1 -
>  drivers/net/ethernet/atheros/alx/Makefile                 | 1 -
>  drivers/net/ethernet/freescale/Makefile                   | 2 --
>  drivers/net/wireless/ath/Makefile                         | 2 --
>  drivers/net/wireless/ath/wil6210/Makefile                 | 2 --
>  drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile | 2 --
>  drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile | 1 -
>  drivers/net/wireless/intel/iwlegacy/Makefile              | 2 --
>  drivers/net/wireless/intel/iwlwifi/Makefile               | 2 +-
>  drivers/net/wireless/intel/iwlwifi/dvm/Makefile           | 2 +-
>  drivers/net/wireless/intel/iwlwifi/mvm/Makefile           | 2 +-

For the drivers/net/wireless/intel/iwlwifi/ part:

Acked-by: Luca Coelho <luciano.coelho-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>

--
Luca.

^ permalink raw reply

* Re: [PATCH 8/8] Makefile: drop -D__CHECK_ENDIAN__ from cflags
From: Arend Van Spriel @ 2016-12-15 20:15 UTC (permalink / raw)
  To: Michael S. Tsirkin, linux-kernel
  Cc: Marcel Holtmann, Gustavo Padovan, Johan Hedberg,
	Wolfgang Grandegger, Marc Kleine-Budde, Vince Bridgers,
	Jay Cliburn, Chris Snook, Luis R. Rodriguez, Kalle Valo,
	Maya Erez, Franky Lin, Hante Meuleman, Stanislaw Gruszka,
	Johannes Berg, Emmanuel Grumbach, Luca Coelho,
	Intel Linux Wireless, Jakub
In-Reply-To: <1481778865-27667-9-git-send-email-mst@redhat.com>

On 15-12-2016 6:15, Michael S. Tsirkin wrote:
> That's the default now, no need for makefiles to set it.
> 
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
>  drivers/bluetooth/Makefile                                | 2 --
>  drivers/net/can/Makefile                                  | 1 -
>  drivers/net/ethernet/altera/Makefile                      | 1 -
>  drivers/net/ethernet/atheros/alx/Makefile                 | 1 -
>  drivers/net/ethernet/freescale/Makefile                   | 2 --
>  drivers/net/wireless/ath/Makefile                         | 2 --
>  drivers/net/wireless/ath/wil6210/Makefile                 | 2 --
>  drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile | 2 --
>  drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile | 1 -

For brcm80211 drivers:

Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>

Regards,
Arend

^ permalink raw reply

* Re: [PATCH 8/8] Makefile: drop -D__CHECK_ENDIAN__ from cflags
From: Greg Kroah-Hartman @ 2016-12-15 11:50 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Emmanuel Grumbach, Stanislaw Gruszka, Gustavo Padovan,
	Jay Cliburn, Luca Coelho, devel, Jakub Kicinski, Stefan Schmidt,
	wil6210, Kalle Valo, Chris Snook, Wolfgang Grandegger,
	Arend van Spriel, Johan Hedberg, Johannes Berg,
	Intel Linux Wireless, Alexander Aring, Marcel Holtmann,
	Hante Meuleman, linux-can, linux-mediatek, nios2-dev,
	Matthias Brugger
In-Reply-To: <1481778865-27667-9-git-send-email-mst@redhat.com>

On Thu, Dec 15, 2016 at 07:15:30AM +0200, Michael S. Tsirkin wrote:
> That's the default now, no need for makefiles to set it.
> 
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
>  drivers/bluetooth/Makefile                                | 2 --
>  drivers/net/can/Makefile                                  | 1 -
>  drivers/net/ethernet/altera/Makefile                      | 1 -
>  drivers/net/ethernet/atheros/alx/Makefile                 | 1 -
>  drivers/net/ethernet/freescale/Makefile                   | 2 --
>  drivers/net/wireless/ath/Makefile                         | 2 --
>  drivers/net/wireless/ath/wil6210/Makefile                 | 2 --
>  drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile | 2 --
>  drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile | 1 -
>  drivers/net/wireless/intel/iwlegacy/Makefile              | 2 --
>  drivers/net/wireless/intel/iwlwifi/Makefile               | 2 +-
>  drivers/net/wireless/intel/iwlwifi/dvm/Makefile           | 2 +-
>  drivers/net/wireless/intel/iwlwifi/mvm/Makefile           | 2 +-
>  drivers/net/wireless/intersil/orinoco/Makefile            | 3 ---
>  drivers/net/wireless/mediatek/mt7601u/Makefile            | 2 --
>  drivers/net/wireless/realtek/rtlwifi/Makefile             | 2 --
>  drivers/net/wireless/realtek/rtlwifi/btcoexist/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8188ee/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192c/Makefile    | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192ce/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192cu/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192de/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192ee/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192se/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8723ae/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8723be/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8723com/Makefile  | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8821ae/Makefile   | 2 --
>  drivers/net/wireless/ti/wl1251/Makefile                   | 2 --
>  drivers/net/wireless/ti/wlcore/Makefile                   | 2 --
>  drivers/staging/rtl8188eu/Makefile                        | 2 +-
>  drivers/staging/rtl8192e/Makefile                         | 2 --
>  drivers/staging/rtl8192e/rtl8192e/Makefile                | 2 --
>  net/bluetooth/Makefile                                    | 2 --
>  net/ieee802154/Makefile                                   | 2 --
>  net/mac80211/Makefile                                     | 2 +-
>  net/mac802154/Makefile                                    | 2 --
>  net/wireless/Makefile                                     | 2 --
>  38 files changed, 5 insertions(+), 68 deletions(-)

For drivers/staging:

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

^ permalink raw reply

* Re: [PATCH 8/8] Makefile: drop -D__CHECK_ENDIAN__ from cflags
From: Marc Kleine-Budde @ 2016-12-15  7:57 UTC (permalink / raw)
  To: Michael S. Tsirkin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	brcm80211-dev-list.pdl-dY08KVG/lbpWk0Htik3J/w, Alexander Aring,
	Stefan Schmidt, linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	linux-can-u79uwXL29TY76Z2rM5mHXA,
	linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	netdev-u79uwXL29TY76Z2rM5mHXA, Matthias Brugger,
	nios2-dev-g9ZBwUv/Ih/yUk5EbOjzuce+I+R0W71w,
	wil6210-A+ZNKFmMK5xy9aJCnZT0Uw, linux-wpan-u79uwXL29TY76Z2rM5mHXA,
	David S. Miller,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1481778865-27667-9-git-send-email-mst-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>


[-- Attachment #1.1.1: Type: text/plain, Size: 685 bytes --]

On 12/15/2016 06:15 AM, Michael S. Tsirkin wrote:
> That's the default now, no need for makefiles to set it.
> 
> Signed-off-by: Michael S. Tsirkin <mst-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> ---
[...]
>  drivers/net/can/Makefile                                  | 1 -

For drivers/net/can/Makefile:

Acked-by: Marc Kleine-Budde <mkl-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

regards,
Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 200 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply

* Re: ti hecc rx frames out of order
From: Wolfgang Grandegger @ 2016-12-15  7:45 UTC (permalink / raw)
  To: Grim, Dennis, linux-can@vger.kernel.org
In-Reply-To: <1579A5A5423CC14B827884B535F8E9513494B0BC@SSCOXCHG2.spray.com>

Hello Denis,

Am 14.12.2016 um 19:44 schrieb Grim, Dennis:
... snip ...
>  
> Thank you Wolfgang.
> 
> I tried your second patch.  It did not correct the o-o-o issue.
> 
> Are there other ideas?
> 
> I am looking at ti_hecc.c.  The HECC and NAPI are new to me so my progress is slow.

OK. Then we need some instrumentation. The history of the mailbox
numbers would be interesting. The following patch may help to find
the problem:


diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c
index 7481c32..b04644d 100644
--- a/drivers/net/can/sja1000/ems_pci.c
+++ b/drivers/net/can/sja1000/ems_pci.c
@@ -115,7 +115,7 @@ MODULE_DEVICE_TABLE(pci, ems_pci_tbl);
 /*
  * Helper to read internal registers from card logic (not CAN)
  */
-static u8 ems_pci_v1_readb(struct ems_pci_card *card, unsigned int port)
+static u8 ems_pci_v1_readb(const struct ems_pci_card *card, unsigned int port)
 {
 	return readb(card->base_addr + (port * 4));
 }
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 680d1ff..1b823b6 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -532,15 +532,20 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev)
 		netif_stop_queue(ndev);
 	}
 	hecc_set_bit(priv, HECC_CANME, mbx_mask);
-	spin_unlock_irqrestore(&priv->mbx_lock, flags);
 
 	hecc_clear_bit(priv, HECC_CANMD, mbx_mask);
 	hecc_set_bit(priv, HECC_CANMIM, mbx_mask);
+	spin_unlock_irqrestore(&priv->mbx_lock, flags);
 	hecc_write(priv, HECC_CANTRS, mbx_mask);
 
 	return NETDEV_TX_OK;
 }
 
+static RX_MBX_HIST_MAX 16
+static unsigned char rx_data0_last;
+static int rx_mbx_hist_idx;
+static int rx_mbx_history[RX_MBX_HIST_MAX];
+
 static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno)
 {
 	struct net_device_stats *stats = &priv->ndev->stats;
@@ -573,6 +578,24 @@ static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno)
 		data = hecc_read_mbx(priv, mbxno, HECC_CANMDH);
 		*(__be32 *)(cf->data + 4) = cpu_to_be32(data);
 	}
+
+	rx_mbx_history[rx_mbx_hist_idx++] = mbxno;
+	if (rx_mbx_hist_idx >= RX_MBX_HIST_MAX)
+		rx_mbx_hist_idx = 0;
+	
+	if (cf->can_id == 0x77) {
+		if (cf->data[0] != (rx_bx_last + 1)) {
+			int i;
+			pr_info("O-O at priv->rx_next = %d\n", priv->rx_next);
+			pr_info("rx_mbx_hist_idx = %d\n", rx_mbx_hist_idx);
+			for (i = 0; i < RX_MBX_HIST_MAX; i++)
+				pr_info("rx_mbx_history[%d] = %d\n",
+					i, rx_mbx_history[i]);
+			
+		}
+		rx_bx_last = cf->data[0];
+	}
+
 	spin_lock_irqsave(&priv->mbx_lock, flags);
 	hecc_clear_bit(priv, HECC_CANME, mbx_mask);
 	hecc_write(priv, HECC_CANRMP, mbx_mask);
@@ -649,9 +672,11 @@ static int ti_hecc_rx_poll(struct napi_struct *napi, int quota)
 	if (hecc_read(priv, HECC_CANRMP) == 0) {
 		napi_complete(napi);
 		/* Re-enable RX mailbox interrupts */
+		spin_lock_irqsave(&priv->mbx_lock, flags);
 		mbx_mask = hecc_read(priv, HECC_CANMIM);
 		mbx_mask |= HECC_TX_MBOX_MASK;
 		hecc_write(priv, HECC_CANMIM, mbx_mask);
+		spin_unlock_irqrestore(&priv->mbx_lock, flags);
 	}
 
 	return num_pkts;
@@ -776,9 +801,9 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
 			mbx_mask = BIT(mbxno);
 			if (!(mbx_mask & hecc_read(priv, HECC_CANTA)))
 				break;
+			spin_lock_irqsave(&priv->mbx_lock, flags);
 			hecc_clear_bit(priv, HECC_CANMIM, mbx_mask);
 			hecc_write(priv, HECC_CANTA, mbx_mask);
-			spin_lock_irqsave(&priv->mbx_lock, flags);
 			hecc_clear_bit(priv, HECC_CANME, mbx_mask);
 			spin_unlock_irqrestore(&priv->mbx_lock, flags);
 			stats->tx_bytes += hecc_read_mbx(priv, mbxno,
@@ -798,9 +823,11 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
 
 		/* Disable RX mailbox interrupts and let NAPI reenable them */
 		if (hecc_read(priv, HECC_CANRMP)) {
+			spin_lock_irqsave(&priv->mbx_lock, flags);
 			ack = hecc_read(priv, HECC_CANMIM);
 			ack &= BIT(HECC_MAX_TX_MBOX) - 1;
 			hecc_write(priv, HECC_CANMIM, ack);
+			spin_unlock_irqrestore(&priv->mbx_lock, flags);
 			napi_schedule(&priv->napi);
 		}
 	}

^ permalink raw reply related

* Re: [PATCH 8/8] Makefile: drop -D__CHECK_ENDIAN__ from cflags
From: Marcel Holtmann @ 2016-12-15  7:00 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: LKML, Gustavo F. Padovan, Johan Hedberg, Wolfgang Grandegger,
	Marc Kleine-Budde, Vince Bridgers, Jay Cliburn, Chris Snook,
	Luis R. Rodriguez, Kalle Valo, Maya Erez, Arend van Spriel,
	Franky Lin, Hante Meuleman, Stanislaw Gruszka, Johannes Berg,
	Emmanuel Grumbach, Luca Coelho, Int
In-Reply-To: <1481778865-27667-9-git-send-email-mst@redhat.com>

Hi Michael,

> That's the default now, no need for makefiles to set it.
> 
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
> drivers/bluetooth/Makefile                                | 2 --
> drivers/net/can/Makefile                                  | 1 -
> drivers/net/ethernet/altera/Makefile                      | 1 -
> drivers/net/ethernet/atheros/alx/Makefile                 | 1 -
> drivers/net/ethernet/freescale/Makefile                   | 2 --
> drivers/net/wireless/ath/Makefile                         | 2 --
> drivers/net/wireless/ath/wil6210/Makefile                 | 2 --
> drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile | 2 --
> drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile | 1 -
> drivers/net/wireless/intel/iwlegacy/Makefile              | 2 --
> drivers/net/wireless/intel/iwlwifi/Makefile               | 2 +-
> drivers/net/wireless/intel/iwlwifi/dvm/Makefile           | 2 +-
> drivers/net/wireless/intel/iwlwifi/mvm/Makefile           | 2 +-
> drivers/net/wireless/intersil/orinoco/Makefile            | 3 ---
> drivers/net/wireless/mediatek/mt7601u/Makefile            | 2 --
> drivers/net/wireless/realtek/rtlwifi/Makefile             | 2 --
> drivers/net/wireless/realtek/rtlwifi/btcoexist/Makefile   | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8188ee/Makefile   | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8192c/Makefile    | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8192ce/Makefile   | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8192cu/Makefile   | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8192de/Makefile   | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8192ee/Makefile   | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8192se/Makefile   | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8723ae/Makefile   | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8723be/Makefile   | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8723com/Makefile  | 2 --
> drivers/net/wireless/realtek/rtlwifi/rtl8821ae/Makefile   | 2 --
> drivers/net/wireless/ti/wl1251/Makefile                   | 2 --
> drivers/net/wireless/ti/wlcore/Makefile                   | 2 --
> drivers/staging/rtl8188eu/Makefile                        | 2 +-
> drivers/staging/rtl8192e/Makefile                         | 2 --
> drivers/staging/rtl8192e/rtl8192e/Makefile                | 2 --
> net/bluetooth/Makefile                                    | 2 --
> net/ieee802154/Makefile                                   | 2 --
> net/mac80211/Makefile                                     | 2 +-
> net/mac802154/Makefile                                    | 2 --
> net/wireless/Makefile                                     | 2 --
> 38 files changed, 5 insertions(+), 68 deletions(-)

for drivers/bluetooth, net/bluetooth, net/ieee802154 and net/mac802154

Acked-by: Marcel Holtmann <marcel@holtmann.org>

Regards

Marcel


^ permalink raw reply

* Re: [PATCH 8/8] Makefile: drop -D__CHECK_ENDIAN__ from cflags
From: Kalle Valo @ 2016-12-15  6:43 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Emmanuel Grumbach, Stanislaw Gruszka, Gustavo Padovan,
	Arend van Spriel, Luca Coelho, David S. Mi  ller, devel,
	Jakub Kicinski, Stefan Schmidt, linux-mediatek, wil6210,
	Chris Snook, Wolfgang Grandegger, Jay Cliburn, linux-wpan,
	Johan Hedberg, Johannes Berg, Intel Linux Wireless,
	Alexander Aring, Marcel Holtmann, Hante Meuleman, linux-can,
	Marc Kleine-Budde
In-Reply-To: <1481778865-27667-9-git-send-email-mst@redhat.com>

"Michael S. Tsirkin" <mst@redhat.com> writes:

> That's the default now, no need for makefiles to set it.
>
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
>  drivers/bluetooth/Makefile                                | 2 --
>  drivers/net/can/Makefile                                  | 1 -
>  drivers/net/ethernet/altera/Makefile                      | 1 -
>  drivers/net/ethernet/atheros/alx/Makefile                 | 1 -
>  drivers/net/ethernet/freescale/Makefile                   | 2 --
>  drivers/net/wireless/ath/Makefile                         | 2 --
>  drivers/net/wireless/ath/wil6210/Makefile                 | 2 --
>  drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile | 2 --
>  drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile | 1 -
>  drivers/net/wireless/intel/iwlegacy/Makefile              | 2 --
>  drivers/net/wireless/intel/iwlwifi/Makefile               | 2 +-
>  drivers/net/wireless/intel/iwlwifi/dvm/Makefile           | 2 +-
>  drivers/net/wireless/intel/iwlwifi/mvm/Makefile           | 2 +-
>  drivers/net/wireless/intersil/orinoco/Makefile            | 3 ---
>  drivers/net/wireless/mediatek/mt7601u/Makefile            | 2 --
>  drivers/net/wireless/realtek/rtlwifi/Makefile             | 2 --
>  drivers/net/wireless/realtek/rtlwifi/btcoexist/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8188ee/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192c/Makefile    | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192ce/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192cu/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192de/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192ee/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8192se/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8723ae/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8723be/Makefile   | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8723com/Makefile  | 2 --
>  drivers/net/wireless/realtek/rtlwifi/rtl8821ae/Makefile   | 2 --
>  drivers/net/wireless/ti/wl1251/Makefile                   | 2 --
>  drivers/net/wireless/ti/wlcore/Makefile                   | 2 --
>  drivers/staging/rtl8188eu/Makefile                        | 2 +-
>  drivers/staging/rtl8192e/Makefile                         | 2 --
>  drivers/staging/rtl8192e/rtl8192e/Makefile                | 2 --
>  net/bluetooth/Makefile                                    | 2 --
>  net/ieee802154/Makefile                                   | 2 --
>  net/mac80211/Makefile                                     | 2 +-
>  net/mac802154/Makefile                                    | 2 --
>  net/wireless/Makefile                                     | 2 --
>  38 files changed, 5 insertions(+), 68 deletions(-)

For drivers/net/wireless:

Acked-by: Kalle Valo <kvalo@codeaurora.org>

-- 
Kalle Valo

^ permalink raw reply

* [PATCH 8/8] Makefile: drop -D__CHECK_ENDIAN__ from cflags
From: Michael S. Tsirkin @ 2016-12-15  5:15 UTC (permalink / raw)
  To: linux-kernel
  Cc: Emmanuel Grumbach, Stanislaw Gruszka, Gustavo Padovan,
	Arend van Spriel, Luca Coelho, devel, Jakub Kicinski,
	Stefan Schmidt, linux-mediatek, wil6210, linux-arm-kernel,
	Chris Snook, Wolfgang Grandegger, Jay Cliburn, linux-wpan,
	Johan Hedberg, Johannes Berg, Intel Linux Wireless,
	Alexander Aring, Marcel Holtmann, Hante Meuleman, linux-can,
	Marc Kleine-Budde, Matthi
In-Reply-To: <1481778865-27667-1-git-send-email-mst@redhat.com>

That's the default now, no need for makefiles to set it.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 drivers/bluetooth/Makefile                                | 2 --
 drivers/net/can/Makefile                                  | 1 -
 drivers/net/ethernet/altera/Makefile                      | 1 -
 drivers/net/ethernet/atheros/alx/Makefile                 | 1 -
 drivers/net/ethernet/freescale/Makefile                   | 2 --
 drivers/net/wireless/ath/Makefile                         | 2 --
 drivers/net/wireless/ath/wil6210/Makefile                 | 2 --
 drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile | 2 --
 drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile | 1 -
 drivers/net/wireless/intel/iwlegacy/Makefile              | 2 --
 drivers/net/wireless/intel/iwlwifi/Makefile               | 2 +-
 drivers/net/wireless/intel/iwlwifi/dvm/Makefile           | 2 +-
 drivers/net/wireless/intel/iwlwifi/mvm/Makefile           | 2 +-
 drivers/net/wireless/intersil/orinoco/Makefile            | 3 ---
 drivers/net/wireless/mediatek/mt7601u/Makefile            | 2 --
 drivers/net/wireless/realtek/rtlwifi/Makefile             | 2 --
 drivers/net/wireless/realtek/rtlwifi/btcoexist/Makefile   | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8188ee/Makefile   | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8192c/Makefile    | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8192ce/Makefile   | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8192cu/Makefile   | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8192de/Makefile   | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8192ee/Makefile   | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8192se/Makefile   | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8723ae/Makefile   | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8723be/Makefile   | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8723com/Makefile  | 2 --
 drivers/net/wireless/realtek/rtlwifi/rtl8821ae/Makefile   | 2 --
 drivers/net/wireless/ti/wl1251/Makefile                   | 2 --
 drivers/net/wireless/ti/wlcore/Makefile                   | 2 --
 drivers/staging/rtl8188eu/Makefile                        | 2 +-
 drivers/staging/rtl8192e/Makefile                         | 2 --
 drivers/staging/rtl8192e/rtl8192e/Makefile                | 2 --
 net/bluetooth/Makefile                                    | 2 --
 net/ieee802154/Makefile                                   | 2 --
 net/mac80211/Makefile                                     | 2 +-
 net/mac802154/Makefile                                    | 2 --
 net/wireless/Makefile                                     | 2 --
 38 files changed, 5 insertions(+), 68 deletions(-)

diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index b1fc29a..8062718 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -40,5 +40,3 @@ hci_uart-$(CONFIG_BT_HCIUART_QCA)	+= hci_qca.o
 hci_uart-$(CONFIG_BT_HCIUART_AG6XX)	+= hci_ag6xx.o
 hci_uart-$(CONFIG_BT_HCIUART_MRVL)	+= hci_mrvl.o
 hci_uart-objs				:= $(hci_uart-y)
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 26ba4b7..7a85495 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -31,5 +31,4 @@ obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
 obj-$(CONFIG_CAN_XILINXCAN)	+= xilinx_can.o
 obj-$(CONFIG_PCH_CAN)		+= pch_can.o
 
-subdir-ccflags-y += -D__CHECK_ENDIAN__
 subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
diff --git a/drivers/net/ethernet/altera/Makefile b/drivers/net/ethernet/altera/Makefile
index 3eff2fd..d4a187e 100644
--- a/drivers/net/ethernet/altera/Makefile
+++ b/drivers/net/ethernet/altera/Makefile
@@ -5,4 +5,3 @@
 obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
 altera_tse-objs := altera_tse_main.o altera_tse_ethtool.o \
 altera_msgdma.o altera_sgdma.o altera_utils.o
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/ethernet/atheros/alx/Makefile b/drivers/net/ethernet/atheros/alx/Makefile
index 5901fa4..ed4a605 100644
--- a/drivers/net/ethernet/atheros/alx/Makefile
+++ b/drivers/net/ethernet/atheros/alx/Makefile
@@ -1,3 +1,2 @@
 obj-$(CONFIG_ALX) += alx.o
 alx-objs := main.o ethtool.o hw.o
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
index 4a13115..c46df5c 100644
--- a/drivers/net/ethernet/freescale/Makefile
+++ b/drivers/net/ethernet/freescale/Makefile
@@ -4,8 +4,6 @@
 
 obj-$(CONFIG_FEC) += fec.o
 fec-objs :=fec_main.o fec_ptp.o
-CFLAGS_fec_main.o := -D__CHECK_ENDIAN__
-CFLAGS_fec_ptp.o := -D__CHECK_ENDIAN__
 
 obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
 ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile
index 89f8d59..4cdebc7 100644
--- a/drivers/net/wireless/ath/Makefile
+++ b/drivers/net/wireless/ath/Makefile
@@ -19,6 +19,4 @@ ath-objs :=	main.o \
 ath-$(CONFIG_ATH_DEBUG) += debug.o
 ath-$(CONFIG_ATH_TRACEPOINTS) += trace.o
 
-ccflags-y += -D__CHECK_ENDIAN__
-
 CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index 11b544b..89bf2f9 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -22,5 +22,3 @@ wil6210-y += p2p.o
 
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
-
-subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
index d1568be..0383ba5 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
@@ -19,8 +19,6 @@ ccflags-y += \
 	-Idrivers/net/wireless/broadcom/brcm80211/brcmfmac	\
 	-Idrivers/net/wireless/broadcom/brcm80211/include
 
-ccflags-y += -D__CHECK_ENDIAN__
-
 obj-$(CONFIG_BRCMFMAC) += brcmfmac.o
 brcmfmac-objs += \
 		cfg80211.o \
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
index 960e6b8..ed83f33 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
@@ -16,7 +16,6 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 ccflags-y := \
-	-D__CHECK_ENDIAN__ \
 	-Idrivers/net/wireless/broadcom/brcm80211/brcmsmac \
 	-Idrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy \
 	-Idrivers/net/wireless/broadcom/brcm80211/include
diff --git a/drivers/net/wireless/intel/iwlegacy/Makefile b/drivers/net/wireless/intel/iwlegacy/Makefile
index c985a01..c826a6b 100644
--- a/drivers/net/wireless/intel/iwlegacy/Makefile
+++ b/drivers/net/wireless/intel/iwlegacy/Makefile
@@ -13,5 +13,3 @@ iwl4965-$(CONFIG_IWLEGACY_DEBUGFS) += 4965-debug.o
 obj-$(CONFIG_IWL3945)	+= iwl3945.o
 iwl3945-objs		:= 3945-mac.o 3945.o 3945-rs.o
 iwl3945-$(CONFIG_IWLEGACY_DEBUGFS) += 3945-debug.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile
index 6e7ed90..92e6118 100644
--- a/drivers/net/wireless/intel/iwlwifi/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/Makefile
@@ -15,7 +15,7 @@ iwlwifi-objs += $(iwlwifi-m)
 
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
 
-ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
+ccflags-y += -I$(src)
 
 obj-$(CONFIG_IWLDVM)	+= dvm/
 obj-$(CONFIG_IWLMVM)	+= mvm/
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/Makefile b/drivers/net/wireless/intel/iwlwifi/dvm/Makefile
index 4d19685..b256a354 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/Makefile
@@ -10,4 +10,4 @@ iwldvm-objs		+= rxon.o devices.o
 iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o
 iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 
-ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
+ccflags-y += -I$(src)/../
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile
index 2e06dfc..83ac807 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile
@@ -9,4 +9,4 @@ iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
 iwlmvm-y += tof.o fw-dbg.o
 iwlmvm-$(CONFIG_PM) += d3.o
 
-ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
+ccflags-y += -I$(src)/../
diff --git a/drivers/net/wireless/intersil/orinoco/Makefile b/drivers/net/wireless/intersil/orinoco/Makefile
index bfdefb8..b7ecef8 100644
--- a/drivers/net/wireless/intersil/orinoco/Makefile
+++ b/drivers/net/wireless/intersil/orinoco/Makefile
@@ -12,6 +12,3 @@ obj-$(CONFIG_TMD_HERMES)	+= orinoco_tmd.o
 obj-$(CONFIG_NORTEL_HERMES)	+= orinoco_nortel.o
 obj-$(CONFIG_PCMCIA_SPECTRUM)	+= spectrum_cs.o
 obj-$(CONFIG_ORINOCO_USB)	+= orinoco_usb.o
-
-# Orinoco should be endian clean.
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/mediatek/mt7601u/Makefile b/drivers/net/wireless/mediatek/mt7601u/Makefile
index ea9ed8a..08fc802 100644
--- a/drivers/net/wireless/mediatek/mt7601u/Makefile
+++ b/drivers/net/wireless/mediatek/mt7601u/Makefile
@@ -1,5 +1,3 @@
-ccflags-y += -D__CHECK_ENDIAN__
-
 obj-$(CONFIG_MT7601U)	+= mt7601u.o
 
 mt7601u-objs	= \
diff --git a/drivers/net/wireless/realtek/rtlwifi/Makefile b/drivers/net/wireless/realtek/rtlwifi/Makefile
index ad6d3c5..84c2e82 100644
--- a/drivers/net/wireless/realtek/rtlwifi/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/Makefile
@@ -30,5 +30,3 @@ obj-$(CONFIG_RTLBTCOEXIST)	+= btcoexist/
 obj-$(CONFIG_RTL8723_COMMON)	+= rtl8723com/
 obj-$(CONFIG_RTL8821AE)		+= rtl8821ae/
 obj-$(CONFIG_RTL8192EE)		+= rtl8192ee/
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/Makefile b/drivers/net/wireless/realtek/rtlwifi/btcoexist/Makefile
index 47ceecf..d1454d4 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/Makefile
@@ -3,5 +3,3 @@ btcoexist-objs :=	halbtc8723b2ant.o	\
 			rtl_btc.o
 
 obj-$(CONFIG_RTLBTCOEXIST) += btcoexist.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/Makefile
index 676e7de..dae4f0f 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/Makefile
@@ -11,5 +11,3 @@ rtl8188ee-objs :=		\
 		trx.o
 
 obj-$(CONFIG_RTL8188EE) += rtl8188ee.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/Makefile
index aee42d7..0546b75 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/Makefile
@@ -5,5 +5,3 @@ rtl8192c-common-objs :=		\
 		phy_common.o
 
 obj-$(CONFIG_RTL8192C_COMMON) += rtl8192c-common.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/Makefile
index c0cb0cf..577c7ad 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/Makefile
@@ -9,5 +9,3 @@ rtl8192ce-objs :=		\
 		trx.o
 
 obj-$(CONFIG_RTL8192CE) += rtl8192ce.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/Makefile
index ad2de6b..97437da 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/Makefile
@@ -10,5 +10,3 @@ rtl8192cu-objs :=		\
 		trx.o
 
 obj-$(CONFIG_RTL8192CU) += rtl8192cu.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/Makefile
index e3213c8..d0703f2 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/Makefile
@@ -10,5 +10,3 @@ rtl8192de-objs :=		\
 		trx.o
 
 obj-$(CONFIG_RTL8192DE) += rtl8192de.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/Makefile
index 0315eed..f254b9f 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/Makefile
@@ -12,5 +12,3 @@ rtl8192ee-objs :=		\
 
 
 obj-$(CONFIG_RTL8192EE) += rtl8192ee.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/Makefile
index b7eb138..dfa9dbb 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/Makefile
@@ -11,5 +11,3 @@ rtl8192se-objs :=		\
 
 obj-$(CONFIG_RTL8192SE) += rtl8192se.o
 
-ccflags-y += -D__CHECK_ENDIAN__
-
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/Makefile
index 6220672..e7607d2 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/Makefile
@@ -14,5 +14,3 @@ rtl8723ae-objs :=		\
 
 
 obj-$(CONFIG_RTL8723AE) += rtl8723ae.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/Makefile
index a77c341..a841cbd 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/Makefile
@@ -12,5 +12,3 @@ rtl8723be-objs :=		\
 
 
 obj-$(CONFIG_RTL8723BE) += rtl8723be.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723com/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8723com/Makefile
index 345a68a..73da755 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723com/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723com/Makefile
@@ -5,5 +5,3 @@ rtl8723-common-objs :=		\
 		phy_common.o
 
 obj-$(CONFIG_RTL8723_COMMON) += rtl8723-common.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/Makefile b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/Makefile
index f7a26f7..8ca406b 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/Makefile
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/Makefile
@@ -12,5 +12,3 @@ rtl8821ae-objs :=		\
 
 
 obj-$(CONFIG_RTL8821AE) += rtl8821ae.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/ti/wl1251/Makefile b/drivers/net/wireless/ti/wl1251/Makefile
index a5c6328..58b4f93 100644
--- a/drivers/net/wireless/ti/wl1251/Makefile
+++ b/drivers/net/wireless/ti/wl1251/Makefile
@@ -6,5 +6,3 @@ wl1251_sdio-objs	+= sdio.o
 obj-$(CONFIG_WL1251)		+= wl1251.o
 obj-$(CONFIG_WL1251_SPI)	+= wl1251_spi.o
 obj-$(CONFIG_WL1251_SDIO)	+= wl1251_sdio.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/ti/wlcore/Makefile b/drivers/net/wireless/ti/wlcore/Makefile
index 0a69c13..e286713 100644
--- a/drivers/net/wireless/ti/wlcore/Makefile
+++ b/drivers/net/wireless/ti/wlcore/Makefile
@@ -8,5 +8,3 @@ wlcore-$(CONFIG_NL80211_TESTMODE)	+= testmode.o
 obj-$(CONFIG_WLCORE)			+= wlcore.o
 obj-$(CONFIG_WLCORE_SPI)		+= wlcore_spi.o
 obj-$(CONFIG_WLCORE_SDIO)		+= wlcore_sdio.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/staging/rtl8188eu/Makefile b/drivers/staging/rtl8188eu/Makefile
index 29b9834..27af86e 100644
--- a/drivers/staging/rtl8188eu/Makefile
+++ b/drivers/staging/rtl8188eu/Makefile
@@ -53,4 +53,4 @@ r8188eu-y :=				\
 
 obj-$(CONFIG_R8188EU)	:= r8188eu.o
 
-ccflags-y += -D__CHECK_ENDIAN__ -I$(srctree)/$(src)/include
+ccflags-y += -I$(srctree)/$(src)/include
diff --git a/drivers/staging/rtl8192e/Makefile b/drivers/staging/rtl8192e/Makefile
index cb18db7..7101fcc 100644
--- a/drivers/staging/rtl8192e/Makefile
+++ b/drivers/staging/rtl8192e/Makefile
@@ -17,5 +17,3 @@ obj-$(CONFIG_RTLLIB_CRYPTO_TKIP) += rtllib_crypt_tkip.o
 obj-$(CONFIG_RTLLIB_CRYPTO_WEP) += rtllib_crypt_wep.o
 
 obj-$(CONFIG_RTL8192E) += rtl8192e/
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/staging/rtl8192e/rtl8192e/Makefile b/drivers/staging/rtl8192e/rtl8192e/Makefile
index a2c4fb4..176a4a2 100644
--- a/drivers/staging/rtl8192e/rtl8192e/Makefile
+++ b/drivers/staging/rtl8192e/rtl8192e/Makefile
@@ -16,5 +16,3 @@ r8192e_pci-objs :=		\
 	rtl_wx.o		\
 
 obj-$(CONFIG_RTL8192E) += r8192e_pci.o
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index b3ff12e..4bfaa19 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -20,5 +20,3 @@ bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
 bluetooth-$(CONFIG_BT_LEDS) += leds.o
 bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
 bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
-
-subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index 4adfd4d..9b92ade 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -7,5 +7,3 @@ ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
 ieee802154_socket-y := socket.o
 
 CFLAGS_trace.o := -I$(src)
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 0b202b3..2829122 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -61,4 +61,4 @@ rc80211_minstrel_ht-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o
 mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
 mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y)
 
-ccflags-y += -D__CHECK_ENDIAN__ -DDEBUG
+ccflags-y += -DDEBUG
diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile
index 17a51e8..5857bb1 100644
--- a/net/mac802154/Makefile
+++ b/net/mac802154/Makefile
@@ -3,5 +3,3 @@ mac802154-objs		:= main.o rx.o tx.o mac_cmd.o mib.o \
 			   iface.o llsec.o util.o cfg.o trace.o
 
 CFLAGS_trace.o := -I$(src)
-
-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 4c9e39f..816c933 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -17,8 +17,6 @@ cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
 
 CFLAGS_trace.o := -I$(src)
 
-ccflags-y += -D__CHECK_ENDIAN__
-
 $(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk
 	@$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@
 
-- 
MST

^ permalink raw reply related

* RE: ti hecc rx frames out of order
From: Grim, Dennis @ 2016-12-14 18:44 UTC (permalink / raw)
  To: linux-can@vger.kernel.org
In-Reply-To: <37dfc512-58de-535c-ed48-e5b9e67c748c@grandegger.com>

> Hello,
> 
> Am 12.12.2016 um 19:46 schrieb Wolfgang Grandegger:
> > Hello Dennis,
> >
> > I had a closer look to the ti_hecc driver...
> >
> > Am 12.12.2016 um 18:17 schrieb Grim, Dennis:
> >>>> I will gladly try your idea.  However, it appears to address an
> >>>> issue related to
> >>> SMP systems.  The TI AM3517 is single core.  Would you expect your
> >>> idea to have any impact for single core?
> >>>
> >>> No.
> >>>
> >>>> Also, the TI AM3517 HECC device driver uses NAPI.
> >
> > At a first glance, access to HECC_CANMIM seems racy. It's modified in
> > the interrupt handler for TX and in ti_hecc_rx_poll() for RX. It may
> > happen, that the wrong RX mailbox is enabled... even if that's not yet
> > obvious too me. For a quick check, you could try the patch below:
> >
> > diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
> > index 680d1ff..fc4b6c2 100644
> > --- a/drivers/net/can/ti_hecc.c
> > +++ b/drivers/net/can/ti_hecc.c
> > @@ -649,9 +649,11 @@ static int ti_hecc_rx_poll(struct napi_struct *napi, int
> quota)
> >  	if (hecc_read(priv, HECC_CANRMP) == 0) {
> >  		napi_complete(napi);
> >  		/* Re-enable RX mailbox interrupts */
> > +		spin_lock_irqsave(&priv->mbx_lock, flags);
> >  		mbx_mask = hecc_read(priv, HECC_CANMIM);
> >  		mbx_mask |= HECC_TX_MBOX_MASK;
> >  		hecc_write(priv, HECC_CANMIM, mbx_mask);
> > +		spin_unlock_irqrestore(&priv->mbx_lock, flags);
> >  	}
> >
> >  	return num_pkts;
> > @@ -776,9 +778,9 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
> >  			mbx_mask = BIT(mbxno);
> >  			if (!(mbx_mask & hecc_read(priv, HECC_CANTA)))
> >  				break;
> > +			spin_lock_irqsave(&priv->mbx_lock, flags);
> >  			hecc_clear_bit(priv, HECC_CANMIM, mbx_mask);
> >  			hecc_write(priv, HECC_CANTA, mbx_mask);
> > -			spin_lock_irqsave(&priv->mbx_lock, flags);
> >  			hecc_clear_bit(priv, HECC_CANME, mbx_mask);
> >  			spin_unlock_irqrestore(&priv->mbx_lock, flags);
> >  			stats->tx_bytes += hecc_read_mbx(priv, mbxno,
> >
> 
> There are two more critical locations where HECC_CANMIM is modified.
> Should be protected in a similar way:
> 
> diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index
> 680d1ff..aa48253 100644
> --- a/drivers/net/can/ti_hecc.c
> +++ b/drivers/net/can/ti_hecc.c
> @@ -532,10 +532,10 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb,
> struct net_device *ndev)
>  		netif_stop_queue(ndev);
>  	}
>  	hecc_set_bit(priv, HECC_CANME, mbx_mask);
> -	spin_unlock_irqrestore(&priv->mbx_lock, flags);
> 
>  	hecc_clear_bit(priv, HECC_CANMD, mbx_mask);
>  	hecc_set_bit(priv, HECC_CANMIM, mbx_mask);
> +	spin_unlock_irqrestore(&priv->mbx_lock, flags);
>  	hecc_write(priv, HECC_CANTRS, mbx_mask);
> 
>  	return NETDEV_TX_OK;
> @@ -649,9 +649,11 @@ static int ti_hecc_rx_poll(struct napi_struct *napi, int
> quota)
>  	if (hecc_read(priv, HECC_CANRMP) == 0) {
>  		napi_complete(napi);
>  		/* Re-enable RX mailbox interrupts */
> +		spin_lock_irqsave(&priv->mbx_lock, flags);
>  		mbx_mask = hecc_read(priv, HECC_CANMIM);
>  		mbx_mask |= HECC_TX_MBOX_MASK;
>  		hecc_write(priv, HECC_CANMIM, mbx_mask);
> +		spin_unlock_irqrestore(&priv->mbx_lock, flags);
>  	}
> 
>  	return num_pkts;
> @@ -776,9 +778,9 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
>  			mbx_mask = BIT(mbxno);
>  			if (!(mbx_mask & hecc_read(priv, HECC_CANTA)))
>  				break;
> +			spin_lock_irqsave(&priv->mbx_lock, flags);
>  			hecc_clear_bit(priv, HECC_CANMIM, mbx_mask);
>  			hecc_write(priv, HECC_CANTA, mbx_mask);
> -			spin_lock_irqsave(&priv->mbx_lock, flags);
>  			hecc_clear_bit(priv, HECC_CANME, mbx_mask);
>  			spin_unlock_irqrestore(&priv->mbx_lock, flags);
>  			stats->tx_bytes += hecc_read_mbx(priv, mbxno, @@ -798,9
> +800,11 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
> 
>  		/* Disable RX mailbox interrupts and let NAPI reenable them */
>  		if (hecc_read(priv, HECC_CANRMP)) {
> +			spin_lock_irqsave(&priv->mbx_lock, flags);
>  			ack = hecc_read(priv, HECC_CANMIM);
>  			ack &= BIT(HECC_MAX_TX_MBOX) - 1;
>  			hecc_write(priv, HECC_CANMIM, ack);
> +			spin_unlock_irqrestore(&priv->mbx_lock, flags);
>  			napi_schedule(&priv->napi);
>  		}
>  	}
> 
> 
> Wolfgang.
 
Thank you Wolfgang.

I tried your second patch.  It did not correct the o-o-o issue.

Are there other ideas?

I am looking at ti_hecc.c.  The HECC and NAPI are new to me so my progress is slow.


Dennis 


^ permalink raw reply

* DO YOU NEED A LOAN??
From: bancoleite @ 2016-12-13  5:27 UTC (permalink / raw)
  To: Recipients

Are you in need of a loan? Apply for more details.

^ permalink raw reply

* Re: ti hecc rx frames out of order
From: Wolfgang Grandegger @ 2016-12-12 19:11 UTC (permalink / raw)
  To: Grim, Dennis, linux-can@vger.kernel.org
In-Reply-To: <e49d562c-979e-ea1e-bdf1-3fc5b7a0926a@grandegger.com>

Hello,

Am 12.12.2016 um 19:46 schrieb Wolfgang Grandegger:
> Hello Dennis,
> 
> I had a closer look to the ti_hecc driver...
> 
> Am 12.12.2016 um 18:17 schrieb Grim, Dennis:
>>>> I will gladly try your idea.  However, it appears to address an issue related to
>>> SMP systems.  The TI AM3517 is single core.  Would you expect your idea to have
>>> any impact for single core?
>>>
>>> No.
>>>
>>>> Also, the TI AM3517 HECC device driver uses NAPI.
> 
> At a first glance, access to HECC_CANMIM seems racy. It's modified in
> the interrupt handler for TX and in ti_hecc_rx_poll() for RX. It may
> happen, that the wrong RX mailbox is enabled... even if that's not yet
> obvious too me. For a quick check, you could try the patch below:
> 
> diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
> index 680d1ff..fc4b6c2 100644
> --- a/drivers/net/can/ti_hecc.c
> +++ b/drivers/net/can/ti_hecc.c
> @@ -649,9 +649,11 @@ static int ti_hecc_rx_poll(struct napi_struct *napi, int quota)
>  	if (hecc_read(priv, HECC_CANRMP) == 0) {
>  		napi_complete(napi);
>  		/* Re-enable RX mailbox interrupts */
> +		spin_lock_irqsave(&priv->mbx_lock, flags);
>  		mbx_mask = hecc_read(priv, HECC_CANMIM);
>  		mbx_mask |= HECC_TX_MBOX_MASK;
>  		hecc_write(priv, HECC_CANMIM, mbx_mask);
> +		spin_unlock_irqrestore(&priv->mbx_lock, flags);
>  	}
>  
>  	return num_pkts;
> @@ -776,9 +778,9 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
>  			mbx_mask = BIT(mbxno);
>  			if (!(mbx_mask & hecc_read(priv, HECC_CANTA)))
>  				break;
> +			spin_lock_irqsave(&priv->mbx_lock, flags);
>  			hecc_clear_bit(priv, HECC_CANMIM, mbx_mask);
>  			hecc_write(priv, HECC_CANTA, mbx_mask);
> -			spin_lock_irqsave(&priv->mbx_lock, flags);
>  			hecc_clear_bit(priv, HECC_CANME, mbx_mask);
>  			spin_unlock_irqrestore(&priv->mbx_lock, flags);
>  			stats->tx_bytes += hecc_read_mbx(priv, mbxno,
> 

There are two more critical locations where HECC_CANMIM is modified.
Should be protected in a similar way:

diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 680d1ff..aa48253 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -532,10 +532,10 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev)
 		netif_stop_queue(ndev);
 	}
 	hecc_set_bit(priv, HECC_CANME, mbx_mask);
-	spin_unlock_irqrestore(&priv->mbx_lock, flags);
 
 	hecc_clear_bit(priv, HECC_CANMD, mbx_mask);
 	hecc_set_bit(priv, HECC_CANMIM, mbx_mask);
+	spin_unlock_irqrestore(&priv->mbx_lock, flags);
 	hecc_write(priv, HECC_CANTRS, mbx_mask);
 
 	return NETDEV_TX_OK;
@@ -649,9 +649,11 @@ static int ti_hecc_rx_poll(struct napi_struct *napi, int quota)
 	if (hecc_read(priv, HECC_CANRMP) == 0) {
 		napi_complete(napi);
 		/* Re-enable RX mailbox interrupts */
+		spin_lock_irqsave(&priv->mbx_lock, flags);
 		mbx_mask = hecc_read(priv, HECC_CANMIM);
 		mbx_mask |= HECC_TX_MBOX_MASK;
 		hecc_write(priv, HECC_CANMIM, mbx_mask);
+		spin_unlock_irqrestore(&priv->mbx_lock, flags);
 	}
 
 	return num_pkts;
@@ -776,9 +778,9 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
 			mbx_mask = BIT(mbxno);
 			if (!(mbx_mask & hecc_read(priv, HECC_CANTA)))
 				break;
+			spin_lock_irqsave(&priv->mbx_lock, flags);
 			hecc_clear_bit(priv, HECC_CANMIM, mbx_mask);
 			hecc_write(priv, HECC_CANTA, mbx_mask);
-			spin_lock_irqsave(&priv->mbx_lock, flags);
 			hecc_clear_bit(priv, HECC_CANME, mbx_mask);
 			spin_unlock_irqrestore(&priv->mbx_lock, flags);
 			stats->tx_bytes += hecc_read_mbx(priv, mbxno,
@@ -798,9 +800,11 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
 
 		/* Disable RX mailbox interrupts and let NAPI reenable them */
 		if (hecc_read(priv, HECC_CANRMP)) {
+			spin_lock_irqsave(&priv->mbx_lock, flags);
 			ack = hecc_read(priv, HECC_CANMIM);
 			ack &= BIT(HECC_MAX_TX_MBOX) - 1;
 			hecc_write(priv, HECC_CANMIM, ack);
+			spin_unlock_irqrestore(&priv->mbx_lock, flags);
 			napi_schedule(&priv->napi);
 		}
 	}


Wolfgang.

^ permalink raw reply related

* Re: ti hecc rx frames out of order
From: Wolfgang Grandegger @ 2016-12-12 18:46 UTC (permalink / raw)
  To: Grim, Dennis, linux-can@vger.kernel.org
In-Reply-To: <1579A5A5423CC14B827884B535F8E95134949B2C@SSCOXCHG2.spray.com>

Hello Dennis,

I had a closer look to the ti_hecc driver...

Am 12.12.2016 um 18:17 schrieb Grim, Dennis:
>>> I will gladly try your idea.  However, it appears to address an issue related to
>> SMP systems.  The TI AM3517 is single core.  Would you expect your idea to have
>> any impact for single core?
>>
>> No.
>>
>>> Also, the TI AM3517 HECC device driver uses NAPI.

At a first glance, access to HECC_CANMIM seems racy. It's modified in
the interrupt handler for TX and in ti_hecc_rx_poll() for RX. It may
happen, that the wrong RX mailbox is enabled... even if that's not yet
obvious too me. For a quick check, you could try the patch below:

diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 680d1ff..fc4b6c2 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -649,9 +649,11 @@ static int ti_hecc_rx_poll(struct napi_struct *napi, int quota)
 	if (hecc_read(priv, HECC_CANRMP) == 0) {
 		napi_complete(napi);
 		/* Re-enable RX mailbox interrupts */
+		spin_lock_irqsave(&priv->mbx_lock, flags);
 		mbx_mask = hecc_read(priv, HECC_CANMIM);
 		mbx_mask |= HECC_TX_MBOX_MASK;
 		hecc_write(priv, HECC_CANMIM, mbx_mask);
+		spin_unlock_irqrestore(&priv->mbx_lock, flags);
 	}
 
 	return num_pkts;
@@ -776,9 +778,9 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
 			mbx_mask = BIT(mbxno);
 			if (!(mbx_mask & hecc_read(priv, HECC_CANTA)))
 				break;
+			spin_lock_irqsave(&priv->mbx_lock, flags);
 			hecc_clear_bit(priv, HECC_CANMIM, mbx_mask);
 			hecc_write(priv, HECC_CANTA, mbx_mask);
-			spin_lock_irqsave(&priv->mbx_lock, flags);
 			hecc_clear_bit(priv, HECC_CANME, mbx_mask);
 			spin_unlock_irqrestore(&priv->mbx_lock, flags);
 			stats->tx_bytes += hecc_read_mbx(priv, mbxno,



Wolfgang.


^ permalink raw reply related

* RE: ti hecc rx frames out of order
From: Grim, Dennis @ 2016-12-12 17:17 UTC (permalink / raw)
  To: linux-can@vger.kernel.org
In-Reply-To: <83813a5e-b146-2763-1fb3-31d64fcebf4c@hartkopp.net>

> > I will gladly try your idea.  However, it appears to address an issue related to
> SMP systems.  The TI AM3517 is single core.  Would you expect your idea to have
> any impact for single core?
> 
> No.
> 
> > Also, the TI AM3517 HECC device driver uses NAPI.
> 
> Ugh!
> 
> @Marc: When this is a NAPI driver - what could be the problem then?
> 
> Is NAPI probably the root cause of the o-o-o issue here?
> 
> Regards,
> Oliver

I should add that the hardware that I'm using for test also has two memory-mapped SJA1000 controllers.  I'm using the Linux mainlined SJA1000 platform device driver.  That driver does not use NAPI.

The same test that generates o-o-o errors on the TI HECC runs for days without o-o-o errors.

This leads me to believe that the issue that I'm seeing is in NAPI or possibly the TI HECC driver.

Dennis


^ permalink raw reply

* Re: ti hecc rx frames out of order
From: Oliver Hartkopp @ 2016-12-12 17:01 UTC (permalink / raw)
  To: Grim, Dennis, Marc Kleine-Budde; +Cc: linux-can@vger.kernel.org
In-Reply-To: <1579A5A5423CC14B827884B535F8E95134949AB3@SSCOXCHG2.spray.com>

On 12/12/2016 04:34 PM, Grim, Dennis wrote:

> I will gladly try your idea.  However, it appears to address an issue related to SMP systems.  The TI AM3517 is single core.  Would you expect your idea to have any impact for single core?

No.

> Also, the TI AM3517 HECC device driver uses NAPI.

Ugh!

@Marc: When this is a NAPI driver - what could be the problem then?

Is NAPI probably the root cause of the o-o-o issue here?

Regards,
Oliver

^ permalink raw reply

* RE: ti hecc rx frames out of order
From: Grim, Dennis @ 2016-12-12 15:34 UTC (permalink / raw)
  To: linux-can@vger.kernel.org
In-Reply-To: <0b6dc834-db5b-a8e3-1461-f97c7c79ad44@hartkopp.net>

> >>> When running canfdtest on a TI AM3517 HECC, frames randomly appear
> >>> out of
> >> order.
> >>>
> >>> "can-utils" is current (cloned this week from github)
> >>>
> >>> Linux kernel is 3.19.0.
> >>
> >> (..)
> >>
> >>> From Peak PCAN-View:
> >>
> >>> 99904)    594366.8  Rx         0077  8  F3 F4 F5 F6 F7 F8 F9 FA
> >>> 99905)    594367.3  Rx         0078  8  F2 F3 F4 F5 F6 F7 F8 F9
> >>> 99906)    594367.8  Rx         0078  8  F3 F4 F5 F6 F7 F8 F9 FA
> >>> 99907)    594368.3  Rx         0077  8  F4 F5 F6 F7 F8 F9 FA FB
> >>> 99908)    594368.7  Rx         0078  8  F1 F2 F3 F4 F5 F6 F7 F8      NOTE: this
> line
> >> is out of order
> >>> 99909)    594369.2  Rx         0077  8  F5 F6 F7 F8 F9 FA FB FC
> >>> 99910)    594369.7  Rx         0078  8  F4 F5 F6 F7 F8 F9 FA FB
> >>> 99911)    594370.2  Rx         0077  8  F6 F7 F8 F9 FA FB FC FD
> >>
> >> Please correct me if I'm wrong, but when you can see this
> >> out-of-order issue on a different host with a different tool:
> >>
> >> Doesn't this lead to the question of a TX out-of-order instead of a
> >> RX out-of- order??
> >>
> >> Regards,
> >> Oliver
> >>
> >
> > Thank you Oliver for your reply.
> >
> > Pardon me for not providing a clearer description of the issue.
> >
> > I believe that it is RX out-of-order.
> >
> > Peak PCAN-View is a Windows application that can be used to monitor CAN bus
> activity.
> >
> > When run as a host, the can-utils canfdtest application generates CAN frames
> with a fixed ID and with data byte values that monotonically increase by one on
> each successive frame.  The lines marked with an * in the PCAN-View output
> below are frames generated by the host.  Note that the data byte values increment
> monotonically from one frame to the next.
> >
> > 99893)    594360.7  Rx         0077  8  ED EE EF F0 F1 F2 F3 F4   *
> > 99894)    594361.1  Rx         0078  8  EC ED EE EF F0 F1 F2 F3
> > 99895)    594361.6  Rx         0077  8  EE EF F0 F1 F2 F3 F4 F5   *
> > 99896)    594362.1  Rx         0078  8  ED EE EF F0 F1 F2 F3 F4
> > 99897)    594362.5  Rx         0078  8  EE EF F0 F1 F2 F3 F4 F5
> > 99898)    594363.0  Rx         0078  8  EF F0 F1 F2 F3 F4 F5 F6
> > 99899)    594363.5  Rx         0077  8  EF F0 F1 F2 F3 F4 F5 F6   *
> > 99900)    594363.9  Rx         0077  8  F0 F1 F2 F3 F4 F5 F6 F7   *
> > 99901)    594364.4  Rx         0078  8  F0 F1 F2 F3 F4 F5 F6 F7
> > 99902)    594364.9  Rx         0077  8  F1 F2 F3 F4 F5 F6 F7 F8   *
> > 99903)    594365.8  Rx         0077  8  F2 F3 F4 F5 F6 F7 F8 F9   *
> > 99904)    594366.8  Rx         0077  8  F3 F4 F5 F6 F7 F8 F9 FA   *
> > 99905)    594367.3  Rx         0078  8  F2 F3 F4 F5 F6 F7 F8 F9
> > 99906)    594367.8  Rx         0078  8  F3 F4 F5 F6 F7 F8 F9 FA
> > 99907)    594368.3  Rx         0077  8  F4 F5 F6 F7 F8 F9 FA FB   *
> > 99908)    594368.7  Rx         0078  8  F1 F2 F3 F4 F5 F6 F7 F8
> > 99909)    594369.2  Rx         0077  8  F5 F6 F7 F8 F9 FA FB FC   *
> > 99910)    594369.7  Rx         0078  8  F4 F5 F6 F7 F8 F9 FA FB
> > 99911)    594370.2  Rx         0077  8  F6 F7 F8 F9 FA FB FC FD   *
> >
> > When run as a device under test (DUT), the can-utils canfdtest application simply
> receives CAN frames and (optionally) outputs them to the console then increments
> the ID and each data byte value by one and sends them.  Note that in the canfdtest
> DUT output below (for frames received from the canfdtest host) lines appear in
> order except for the line marked with *.
> >
> > 0077: [8] ed ee ef f0 f1 f2 f3 f4
> > 0077: [8] ee ef f0 f1 f2 f3 f4 f5
> > 0077: [8] ef f0 f1 f2 f3 f4 f5 f6
> > 0077: [8] f1 f2 f3 f4 f5 f6 f7 f8
> > 0077: [8] f2 f3 f4 f5 f6 f7 f8 f9
> > 0077: [8] f0 f1 f2 f3 f4 f5 f6 f7   *
> > 0077: [8] f3 f4 f5 f6 f7 f8 f9 fa
> > 0077: [8] f4 f5 f6 f7 f8 f9 fa fb
> > 0077: [8] f5 f6 f7 f8 f9 fa fb fc
> >
> > Since the frames were sent in order by the canfdtest host but reported out of
> order by the canfdtest DUT, it seems that it is an RX issue.
> >
> > I have been away for a few days but plan to investigate the issue now.
> >
> > Any help with this issue would be most welcome.
> 
> Hi Dennis,
> 
> can you check whether this idea helps you in your setup:
> 
> http://marc.info/?l=linux-can&m=148007442317274&w=2
> 
> Regards,
> Oliver


Thank you.

I will gladly try your idea.  However, it appears to address an issue related to SMP systems.  The TI AM3517 is single core.  Would you expect your idea to have any impact for single core?

Also, the TI AM3517 HECC device driver uses NAPI.

Dennis



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox