lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Wed, 14 Mar 2018 08:51:50 +0100
From:   Marc Kleine-Budde <mkl@...gutronix.de>
To:     Jakob Unterwurzacher <jakob.unterwurzacher@...obroma-systems.com>
Cc:     Martin Elshuber <martin.elshuber@...obroma-systems.com>,
        Philipp Tomsich <philipp.tomsich@...obroma-systems.com>,
        Wolfgang Grandegger <wg@...ndegger.com>,
        linux-can@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH v2 1/1] can: ucan: add driver for Theobroma Systems UCAN
 devices

On 03/13/2018 06:35 PM, Jakob Unterwurzacher wrote:
> The UCAN driver supports the microcontroller-based USB/CAN
> adapters from Theobroma Systems. There are two form-factors
> that run essentially the same firmware:
> 
> * Seal: standalone USB stick ( https://www.theobroma-systems.com/seal )
> 
> * Mule: integrated on the PCB of various System-on-Modules from
>   Theobroma Systems like the A31-µQ7 and the RK3399-Q7
>   ( https://www.theobroma-systems.com/rk3399-q7 )
> 
> The USB wire protocol has been designed to be as generic and
> hardware-indendent as possible in the hope of being useful for
> implementation on other microcontrollers.
> 
> Signed-off-by: Martin Elshuber <martin.elshuber@...obroma-systems.com>
> Signed-off-by: Jakob Unterwurzacher <jakob.unterwurzacher@...obroma-systems.com>
> Signed-off-by: Philipp Tomsich <philipp.tomsich@...obroma-systems.com>
> ---
>  Documentation/networking/can_ucan_protocol.rst |  315 +++++
>  Documentation/networking/index.rst             |    1 +
>  drivers/net/can/usb/Kconfig                    |   10 +
>  drivers/net/can/usb/Makefile                   |    1 +
>  drivers/net/can/usb/ucan.c                     | 1587 ++++++++++++++++++++++++
>  5 files changed, 1914 insertions(+)
>  create mode 100644 Documentation/networking/can_ucan_protocol.rst
>  create mode 100644 drivers/net/can/usb/ucan.c
> 
> diff --git a/Documentation/networking/can_ucan_protocol.rst b/Documentation/networking/can_ucan_protocol.rst
> new file mode 100644
> index 000000000000..d859b36200b4
> --- /dev/null
> +++ b/Documentation/networking/can_ucan_protocol.rst
> @@ -0,0 +1,315 @@
> +=================
> +The UCAN Protocol
> +=================
> +
> +UCAN is the protocol used by the microcontroller-based USB-CAN
> +adapter that is integrated on System-on-Modules from Theobroma Systems
> +and that is also available as a standalone USB stick.
> +
> +The UCAN protocol has been designed to be hardware-independent.
> +It is modeled closely after how Linux represents CAN devices
> +internally. All multi-byte integers are encoded as Little Endian.
> +
> +All structures mentioned in this document are defined in
> +``drivers/net/can/usb/ucan.c``.
> +
> +USB Endpoints
> +=============
> +
> +UCAN devices use three USB endpoints:
> +
> +CONTROL endpoint
> +  The driver sends device management commands on this endpoint
> +
> +IN endpoint
> +  The device sends CAN data frames and CAN error frames
> +
> +OUT endpoint
> +  The driver sends CAN data frames on the out endpoint
> +
> +
> +CONTROL Messages
> +================
> +
> +UCAN devices are configured using vendor requests on the control pipe.
> +
> +To support multiple CAN interfaces in a single USB device all
> +configuration commands target the corresponding interface in the USB
> +descriptor.
> +
> +The driver uses ``ucan_ctrl_command_in/out`` and
> +``ucan_device_request_in`` to deliver commands to the device.
> +
> +Setup Packet
> +------------
> +
> +=================  =====================================================
> +``bmRequestType``  Direction | Vendor | (Interface or Device)
> +``bRequest``       Command Number
> +``wValue``         Subcommand Number (16 Bit) or 0 if not used
> +``wIndex``         USB Interface Index (0 for device commands)
> +``wLength``        * Host to Device - Number of bytes to transmit
> +                   * Device to Host - Maximum Number of bytes to
> +                     receive. If the device send less. Commom ZLP
> +                     semantics are used.
> +=================  =====================================================
> +
> +Error Handling
> +--------------
> +
> +The device indicates failed control commands by stalling the
> +pipe.
> +
> +Device Commands
> +---------------
> +
> +UCAN_DEVICE_GET_FW_STRING
> +~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +*Dev2Host; optional*
> +
> +Request the device firmware string.
> +
> +
> +Interface Commands
> +------------------
> +
> +UCAN_COMMAND_START
> +~~~~~~~~~~~~~~~~~~
> +
> +*Host2Dev; mandatory*
> +
> +Bring the CAN interface up.
> +
> +Payload Format
> +  ``ucan_ctl_payload_t.cmd_start``
> +
> +====  ============================
> +mode  or mask of ``UCAN_MODE_*``
> +====  ============================
> +
> +UCAN_COMMAND_STOP
> +~~~~~~~~~~~~~~~~~~
> +
> +*Host2Dev; mandatory*
> +
> +Stop the CAN interface
> +
> +Payload Format
> +  *empty*
> +
> +UCAN_COMMAND_RESET
> +~~~~~~~~~~~~~~~~~~
> +
> +*Host2Dev; mandatory*
> +
> +Reset the CAN controller (including error counters)
> +
> +Payload Format
> +  *empty*
> +
> +UCAN_COMMAND_GET
> +~~~~~~~~~~~~~~~~
> +
> +*Host2Dev; mandatory*
> +
> +Get Information from the Device
> +
> +Subcommands
> +^^^^^^^^^^^
> +
> +UCAN_COMMAND_GET_INFO
> +  Request the device information structure ``ucan_ctl_payload_t.device_info``.
> +
> +  See the ``device_info`` field for details, and
> +  ``uapi/linux/can/netlink.h`` for an explanation of the
> +  ``can_bittiming fields``.
> +
> +  Payload Format
> +    ``ucan_ctl_payload_t.device_info``
> +
> +UCAN_COMMAND_GET_PROTOCOL_VERSION
> +
> +  Request the device protocol version
> +  ``ucan_ctl_payload_t.protocol_version``. The current protocol version is 3.
> +
> +  Payload Format
> +    ``ucan_ctl_payload_t.protocol_version``
> +
> +.. note:: Devices that do not implement this command use the old
> +          protocol version 1
> +
> +UCAN_COMMAND_SET_BITTIMING
> +~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +*Host2Dev; mandatory*
> +
> +Setup bittiming by sending the the structure
> +``ucan_ctl_payload_t.cmd_set_bittiming`` (see ``struct bittiming`` for
> +details)
> +
> +Payload Format
> +  ``ucan_ctl_payload_t.cmd_set_bittiming``.
> +
> +UCAN_SLEEP/WAKE
> +~~~~~~~~~~~~~~~
> +
> +*Host2Dev; optional*
> +
> +Configure sleep and wake modes. Not yet supported by the driver.
> +
> +UCAN_FILTER
> +~~~~~~~~~~~
> +
> +*Host2Dev; optional*
> +
> +Setup hardware CAN filters. Not yet supported by the driver.
> +
> +Allowed interface commands
> +--------------------------
> +
> +==================  ===================  ==================
> +Legal Device State  Command              New Device State
> +==================  ===================  ==================
> +stopped             SET_BITTIMING        stopped
> +stopped             START                started
> +started             STOP or RESET        stopped
> +stopped             STOP or RESET        stopped
> +started             RESTART              started
> +any                 GET                  *no change*
> +==================  ===================  ==================
> +
> +IN Message Format
> +=================
> +
> +A data packet on the USB IN endpoint contains one or more
> +``ucan_message_in`` values. If multiple messages are batched in a USB
> +data packet, the ``len`` field can be used to jump to the next
> +``ucan_message_in`` value (take care to sanity-check the ``len`` value
> +against the actual data size).
> +
> +.. _can_ucan_in_message_len:
> +
> +``len`` field
> +-------------
> +
> +Each ``ucan_message_in`` must be aligned to a 4-byte boundary (relative
> +to the start of the start of the data buffer). That means that there
> +may be padding bytes between multiple ``ucan_message_in`` values:
> +
> +.. code::
> +
> +    +----------------------------+ < 0
> +    |                            |
> +    |   struct ucan_message_in   |
> +    |                            |
> +    +----------------------------+ < len
> +              [padding]
> +    +----------------------------+ < round_up(len, 4)
> +    |                            |
> +    |   struct ucan_message_in   |
> +    |                            |
> +    +----------------------------+
> +                [...]
> +
> +``type`` field
> +--------------
> +
> +The ``type`` field specifies the type of the message.
> +
> +UCAN_IN_RX
> +~~~~~~~~~~
> +
> +``subtype``
> +  zero
> +
> +Data received from the CAN bus (ID + payload).
> +
> +UCAN_IN_TX_COMPLETE
> +~~~~~~~~~~~~~~~~~~~
> +
> +``subtype``
> +  zero
> +
> +The CAN device has sent a message to the CAN bus. It answers with a
> +set of echo-ids from previous UCAN_OUT_TX messages
> +
> +Flow Control
> +------------
> +
> +When receiving CAN messages there is no flow control on the USB
> +buffer. The driver has to handle inbound message quickly enough to
> +avoid drops. I case the device buffer overflow the condition is
> +reported by sending corresponding error frames (see
> +:ref:`can_ucan_error_handling`)
> +
> +
> +OUT Message Format
> +==================
> +
> +A data packet on the USB OUT endpoint contains one or more ``struct
> +ucan_message_out`` values. If multiple messages are batched into one
> +data packet, the device uses the ``len`` field to jump to the next
> +ucan_message_out value. Each ucan_message_out must be aligned to 4
> +bytes (relative to the start of the data buffer). The mechanism is
> +same as described in :ref:`can_ucan_in_message_len`.
> +
> +.. code::
> +
> +    +----------------------------+ < 0
> +    |                            |
> +    |   struct ucan_message_out  |
> +    |                            |
> +    +----------------------------+ < len
> +              [padding]
> +    +----------------------------+ < round_up(len, 4)
> +    |                            |
> +    |   struct ucan_message_out  |
> +    |                            |
> +    +----------------------------+
> +                [...]
> +
> +``type`` field
> +--------------
> +
> +In protocol version 3 only ``UCAN_OUT_TX`` is defined, others are used
> +only by legacy devices (protocol version 1).
> +
> +UCAN_OUT_TX
> +~~~~~~~~~~~
> +``subtype``
> +  echo id to be replied within a CAN_IN_TX_COMPLETE message
> +
> +Transmit a CAN frame. (parameters: ``id``, ``data``)
> +
> +Flow Control
> +------------
> +
> +When the device outbound buffers are full it starts sending *NAKs* on
> +the *OUT* pipe until more buffers are available. The driver stops the
> +queue when a certain threshold of out packets are incomplete.
> +
> +.. _can_ucan_error_handling:
> +
> +CAN Error Handling
> +==================
> +
> +If error reporting is turned on the device encodes errors into CAN
> +error frames (see ``uapi/linux/can/error.h``) and sends it using the
> +IN endpoint. The driver updates its error statistics and forwards
> +it.
> +
> +Although UCAN devices can suppress error frames completely, in Linux
> +the driver is always interested. Hence, the device is always started with
> +the ``UCAN_MODE_BERR_REPORT`` set. Filtering those messages for the
> +user space is done by the driver.
> +
> +Example Conversation
> +====================
> +
> +#) Device is connected to USB
> +#) Host sends command ``UCAN_COMMAND_RESET``, subcmd 0
> +#) Host sends command ``UCAN_COMMAND_GET``, subcmd ``UCAN_COMMAND_GET_INFO``
> +#) Device sends ``UCAN_IN_DEVICE_INFO``
> +#) Host sends command ``UCAN_OUT_SET_BITTIMING``
> +#) Host sends command ``UCAN_COMMAND_START``, subcmd 0, mode ``UCAN_MODE_BERR_REPORT``
> diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
> index 90966c2692d8..18903968cebf 100644
> --- a/Documentation/networking/index.rst
> +++ b/Documentation/networking/index.rst
> @@ -8,6 +8,7 @@ Contents:
>  
>     batman-adv
>     can
> +   can_ucan_protocol
>     kapi
>     z8530book
>     msg_zerocopy
> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> index c36f4bdcbf4f..490cdce1f1da 100644
> --- a/drivers/net/can/usb/Kconfig
> +++ b/drivers/net/can/usb/Kconfig
> @@ -89,4 +89,14 @@ config CAN_MCBA_USB
>  	  This driver supports the CAN BUS Analyzer interface
>  	  from Microchip (http://www.microchip.com/development-tools/).
>  
> +config CAN_UCAN
> +	tristate "Theobroma Systems UCAN interface"
> +	---help---
> +	  This driver supports the Theobroma Systems
> +	  UCAN USB-CAN interface.
> +
> +	  UCAN is an microcontroller-based USB-CAN interface that
> +	  is integrated on System-on-Modules made by Theobroma Systems
> +	  (https://www.theobroma-systems.com/som-products).
> +
>  endmenu
> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> index 49ac7b99ba32..4176e8358232 100644
> --- a/drivers/net/can/usb/Makefile
> +++ b/drivers/net/can/usb/Makefile
> @@ -10,3 +10,4 @@ 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
> +obj-$(CONFIG_CAN_UCAN) += ucan.o
> diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c
> new file mode 100644
> index 000000000000..61348e8c4747
> --- /dev/null
> +++ b/drivers/net/can/usb/ucan.c
> @@ -0,0 +1,1587 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* Driver for Theobroma Systems UCAN devices Protocol Version 3
> + *
> + * Copyright (C) 2018 Theobroma Systems Design und Consulting GmbH
> + *
> + *
> + * General Description:
> + *
> + * The USB Device uses three Endpoints:
> + *
> + *   CONTROL Endpoint: Is used the setup the device (start, stop,
> + *   info, configure).
> + *
> + *   IN Endpoint: The device sends CAN Frame Messages and Device
> + *   Information using the IN endpoint.
> + *
> + *   OUT Endpoint: The driver sends configuration requests, and CAN
> + *   Frames on the out endpoint.
> + *
> + * Error Handling:
> + *
> + *   If error reporting is turned on the device encodes error into CAN
> + *   error frames (see uapi/linux/can/error.h) and sends it using the
> + *   IN Endpoint. The driver updates statistics and forward it.
> + */
> +
> +#include <linux/can.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/signal.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/usb.h>
> +
> +#include <linux/can.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +
> +#define UCAN_MAX_RX_URBS 8
> +/* the CAN controller needs a while to enable/disable the bus */
> +#define UCAN_USB_CTL_PIPE_TIMEOUT 1000
> +/* this driver currently supports protocol version 3 only */
> +#define UCAN_PROTOCOL_VERSION_MIN 3
> +#define UCAN_PROTOCOL_VERSION_MAX 3
> +
> +/* UCAN Message Definitions --------------------------------------------
> + *
> + *  ucan_message_out_t and ucan_message_in_t define the messages
> + *  transmitted on the OUT and IN endpoint.
> + *
> + *  Multibyte fields are transmitted with little endianness
> + *
> + *  INTR Endpoint: a single uint32_t storing the current space in the fifo
> + *
> + *  OUT Endpoint: single message of type ucan_message_out_t is
> + *    transmitted on the out endpoint
> + *
> + *  IN Endpoint: multiple messages ucan_message_in_t concateted in
> + *    the following way:
> + *
> + *	m[n].len <=> the length if message n(including the header in bytes)
> + *	m[n] is is aligned to a 4 byte boundary, hence
> + *	  offset(m[0])	 := 0;
> + *	  offset(m[n+1]) := offset(m[n]) + (m[n].len + 3) & 3
> + *
> + *	this implies that
> + *	  offset(m[n]) % 4 <=> 0
> + */
> +
> +/* Device Global Commands */
> +enum {
> +	UCAN_DEVICE_GET_FW_STRING = 0,
> +};
> +
> +/* UCAN Commands */
> +enum {
> +	/* start the can transceiver - val defines the operation mode */
> +	UCAN_COMMAND_START    = 0,

Just one spaces in front of the '='.

> +	/* cancel pending transmissions and stop the can transceiver */
> +	UCAN_COMMAND_STOP     = 1,
> +	/* send can transceiver into low-power sleep mode */
> +	UCAN_COMMAND_SLEEP    = 2,
> +	/* wake up can transceiver from low-power sleep mode */
> +	UCAN_COMMAND_WAKEUP   = 3,
> +	/* reset the can transceiver */
> +	UCAN_COMMAND_RESET    = 4,
> +	/* get piece of info from the can transceiver - subcmd defines what
> +	 * piece
> +	 */
> +	UCAN_COMMAND_GET      = 5,
> +	/* clear or disable hardware filter - subcmd defines which of the two */
> +	UCAN_COMMAND_FILTER   = 6,
> +	/* Setup bittiming */
> +	UCAN_COMMAND_SET_BITTIMING = 7,
> +	/* recover from bus-off state */
> +	UCAN_COMMAND_RESTART  = 8,
> +};
> +
> +/* UCAN_COMMAND_START and UCAN_COMMAND_GET_INFO operation modes (bitmap).
> + * Undefined bits must be set to 0.
> + */
> +enum {
> +	UCAN_MODE_LOOPBACK    = (1 << 0),

Use BIT()

> +	UCAN_MODE_SILENT      = (1 << 1),
> +	UCAN_MODE_3_SAMPLES   = (1 << 2),
> +	UCAN_MODE_ONE_SHOT    = (1 << 3),
> +	UCAN_MODE_BERR_REPORT = (1 << 4),
> +};
> +
> +/* UCAN_COMMAND_GET subcommands */
> +enum {
> +	UCAN_COMMAND_GET_INFO             = 0,
> +	UCAN_COMMAND_GET_PROTOCOL_VERSION = 1,
> +};
> +
> +/* UCAN_COMMAND_FILTER subcommands */
> +enum {
> +	UCAN_FILTER_CLEAR     = 0,
> +	UCAN_FILTER_DISABLE   = 1,
> +	UCAN_FILTER_ENABLE    = 2,
> +};
> +
> +/* OUT endpoint message types */
> +enum {
> +	UCAN_OUT_TX = 2, /* transmit a CAN frame */
> +};
> +
> +/* IN endpoint message types */
> +enum {
> +	UCAN_IN_TX_COMPLETE = 1,  /* CAN frame transmission completed */
> +	UCAN_IN_RX          = 2,  /* CAN frame received */
> +};
> +
> +struct ucan_ctl_cmd_start {
> +	u16 mode;            /* oring any of UCAN_MODE_* */

__le16 as discussed in the previous mail.

> +} __packed;
> +
> +struct ucan_ctl_cmd_set_bittiming {
> +	u32 tq;		     /* Time quanta (TQ) in nanoseconds */

__le32

> +	u16 brp;	     /* TQ Prescaler */
> +	u16 sample_point;    /* Samplepoint on tenth percent */
> +	u8 prop_seg;	     /* Propagation segment in TQs */
> +	u8 phase_seg1;	     /* Phase buffer segment 1 in TQs */
> +	u8 phase_seg2;	     /* Phase buffer segment 2 in TQs */
> +	u8 sjw;		     /* Synchronisation jump width in TQs */
> +} __packed;
> +
> +struct ucan_ctl_cmd_device_info {
> +	u32 freq;   /* Clock Frequency for tq generation */
> +	u8 tx_fifo; /* Size of the transmission fifo */
> +	u8 sjw_max;   /* can_bittiming fields... */
> +	u8 tseg1_min;
> +	u8 tseg1_max;
> +	u8 tseg2_min;
> +	u8 tseg2_max;
> +	u16 brp_inc;
> +	u32 brp_min;
> +	u32 brp_max; /* ...can_bittiming fields */
> +	u16 ctrlmodes; /* supported control modes */
> +	u16 hwfilter;  /* Number of HW filter banks */
> +	u16 rxmboxes;  /* Number of receive Mailboxes */
> +} __packed;
> +
> +struct ucan_ctl_cmd_get_protocol_version {
> +	u32 version;
> +} __packed;
> +
> +union ucan_ctl_payload {
> +	/***************************************************
> +	 * Setup Bittiming
> +	 * bmRequest == UCAN_COMMAND_START
> +	 ***************************************************/
> +	struct ucan_ctl_cmd_start cmd_start;
> +	/***************************************************
> +	 * Setup Bittiming
> +	 * bmRequest == UCAN_COMMAND_SET_BITTIMING
> +	 ***************************************************/
> +	struct ucan_ctl_cmd_set_bittiming cmd_set_bittiming;
> +	/***************************************************
> +	 * Get Device Information
> +	 * bmRequest == UCAN_COMMAND_GET; wValue = UCAN_COMMAND_GET_INFO
> +	 ***************************************************/
> +	struct ucan_ctl_cmd_device_info cmd_get_device_info;
> +	/***************************************************
> +	 * Get Protocol Version
> +	 * bmRequest == UCAN_COMMAND_GET;
> +	 * wValue = UCAN_COMMAND_GET_PROTOCOL_VERSION
> +	 ***************************************************/
> +	struct ucan_ctl_cmd_get_protocol_version cmd_get_protocol_version;
> +
> +	u8 raw[128];
> +} __packed;
> +
> +enum {
> +	UCAN_TX_COMPLETE_SUCCESS = (1 << 0),
> +};
> +
> +/* Transmission Complete within ucan_message_in */
> +struct ucan_tx_complete_entry_t {
> +	u8 echo_id;
> +	u8 flags;
> +}  __packed __aligned(0x2);
> +
> +/* CAN Data message format within ucan_message_in/out */
> +struct ucan_can_msg {
> +	/* note DLC is computed by
> +	 *    msg.len - sizeof (msg.len)
> +	 *	       - sizeof (msg.type)
> +	 *	       - sizeof (msg.can_msg.id)
> +	 */
> +	u32 id;
> +
> +	union {
> +		u8 data[CAN_MAX_DLEN];  /* Data of CAN frames */
> +		u8 dlc;                 /* RTR dlc */
> +	};
> +} __packed;
> +
> +/* OUT Endpoint, outbound messages */
> +struct ucan_message_out {
> +	u16 len;  /* Length of the content include header */
> +	u8  type; /* UCAN_OUT_TX and friends */

one space after 'u8'

> +	u8  subtype; /* command sub type */
> +	union {
> +		/***************************************************
> +		 * Transmit CAN frame
> +		 * (type == UCAN_TX) && ((msg.can_msg.id & CAN_RTR_FLAG) == 0)
> +		 * subtype stores the echo id
> +		 ***************************************************/
> +		struct ucan_can_msg can_msg;
> +	} msg;
> +} __packed __aligned(0x4);
> +
> +/* IN Endpoint, inbound messages */
> +struct ucan_message_in {
> +	u16 len;     /* Length of the content include header */
> +	u8  type;    /* UCAN_IN_RX and friends */
> +	u8  subtype; /* command sub type */
> +
> +	union {
> +		/***************************************************
> +		 * CAN Frame received
> +		 * (type == UCAN_IN_RX)
> +		 * && ((msg.can_msg.id & CAN_RTR_FLAG) == 0)
> +		 ***************************************************/
> +		struct ucan_can_msg can_msg;
> +
> +		/***************************************************
> +		 * CAN transmission complete
> +		 * (type == UCAN_IN_TX_COMPLETE)
> +		 ***************************************************/
> +		struct ucan_tx_complete_entry_t can_tx_complete_msg[0];
> +
> +	} __aligned(0x4) msg;
> +} __packed;
> +
> +/* Macros to calculate message lengths */
> +#define UCAN_OUT_HDR_SIZE offsetof(struct ucan_message_out, msg)
> +
> +#define UCAN_IN_HDR_SIZE offsetof(struct ucan_message_in, msg)
> +#define UCAN_IN_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member))
> +
> +struct ucan_priv;
> +
> +/* Context Information for transmission URBs */
> +struct ucan_urb_context {
> +	struct ucan_priv *up;
> +	u32 echo_index;
> +	u8 dlc;
> +	atomic_t allocated;
> +};
> +
> +/* Information reported by the USB device */
> +struct ucan_device_info {
> +	struct can_bittiming_const bittiming_const;
> +	u8 tx_fifo;
> +};
> +
> +/* Driver private data */
> +struct ucan_priv {
> +	struct can_priv can; /* must be the first member */
> +
> +	u8 intf_index;
> +	struct usb_device *udev;
> +	struct usb_interface *intf;
> +	struct net_device *netdev;
> +
> +	struct usb_endpoint_descriptor *out_ep;
> +	struct usb_endpoint_descriptor *in_ep;
> +
> +	struct usb_anchor rx_urbs;
> +	struct usb_anchor tx_urbs;
> +
> +	union ucan_ctl_payload *ctl_msg_buffer;
> +	struct ucan_device_info device_info;
> +
> +	atomic_t available_tx_urbs;
> +	struct ucan_urb_context *tx_contexts;
> +};
> +
> +static u8 ucan_compute_dlc(u16 len, struct ucan_can_msg *msg)

We usually put pointers we're working on as first parameters.

> +{
> +	u16 res = 0;

Why is the function a u8 while res a u16?

> +
> +	if (msg->id & CAN_RTR_FLAG)
> +		res = msg->dlc;
> +	else
> +		res = len - (UCAN_IN_HDR_SIZE + sizeof(msg->id));
> +
> +	if (res > CAN_MAX_DLEN)
> +		return -1;
> +
> +	return res;
> +}
> +
> +static void ucan_release_contexts(struct ucan_priv *up)
> +{
> +	if (!up->tx_contexts)
> +		return;
> +
> +	atomic_set(&up->available_tx_urbs, 0);
> +
> +	kfree(up->tx_contexts);
> +	up->tx_contexts = NULL;
> +}
> +
> +static int ucan_allocate_contexts(struct ucan_priv *up)
> +{
> +	int i;
> +
> +	/* release contexts if any */
> +	ucan_release_contexts(up);
> +
> +	up->tx_contexts = kmalloc_array(up->device_info.tx_fifo,
> +					sizeof(*up->tx_contexts),
> +					GFP_KERNEL);
> +	if (!up->tx_contexts) {
> +		dev_err(&up->udev->dev, "Not enough memory to allocate tx contexts\n");

As fas as I know, the kernel will print an error message if kmalloc fails.

> +		return -ENOMEM;
> +	}
> +
> +	memset(up->tx_contexts, 0,
> +	       sizeof(*up->tx_contexts) * up->device_info.tx_fifo);

use kcalloc(), then you don't need the memset()

> +	for (i = 0; i < up->device_info.tx_fifo; i++) {
> +		atomic_set(&up->tx_contexts[i].allocated, 0);
> +		up->tx_contexts[i].up = up;
> +		up->tx_contexts[i].echo_index = i;
> +	}
> +
> +	atomic_set(&up->available_tx_urbs, up->device_info.tx_fifo);
> +
> +	return 0;
> +}
> +
> +static struct ucan_urb_context *ucan_allocate_context(struct ucan_priv *up)
> +{
> +	int i, allocated, avail;
> +
> +	if (!up->tx_contexts)
> +		return NULL;
> +
> +	for (i = 0; i < up->device_info.tx_fifo; i++) {
> +		allocated = atomic_cmpxchg(&up->tx_contexts[i].allocated, 0, 1);
> +		if (allocated == 0) {

if (!allocated)

> +			avail = atomic_sub_return(1, &up->available_tx_urbs);
> +			if (avail == 0)

if (!avail)

> +				netif_stop_queue(up->netdev);
> +			return &up->tx_contexts[i];
> +		}
> +	}
> +	return NULL;
> +}
> +
> +static void ucan_release_context(struct ucan_priv *up,
> +				 struct ucan_urb_context *ctx)
> +{
> +	WARN_ON_ONCE(!up->tx_contexts);
> +	if (!up->tx_contexts)

if ((WARN_ON_ONCE(!up->tx_contexts))

> +		return;
> +
> +	if (atomic_cmpxchg(&ctx->allocated, 1, 0) == 0) {
> +		dev_warn(&up->udev->dev,
> +			 "context %p (#%ld) was not allocated\n",
> +			 ctx, ctx - up->tx_contexts);

This should also not happen, right? If so, make it WARN_ON_ONCE()

> +	} else {
> +		atomic_inc(&up->available_tx_urbs);
> +		netif_wake_queue(up->netdev);
> +	}
> +}
> +
> +static int ucan_ctrl_command_out(struct ucan_priv *up,
> +				 u8 cmd,
> +				 u16 subcmd,
> +				 size_t datalen)

Why is datalen a size_t? In usb_control_msg() it's a u16.

> +{
> +	if (datalen > sizeof(union ucan_ctl_payload))
> +		return -ENOMEM;

This should or even cannot happen. You call this function directly.

> +
> +	return usb_control_msg(up->udev,
> +			usb_sndctrlpipe(up->udev, 0),
> +			cmd,
> +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
> +			cpu_to_le16(subcmd),
> +			up->intf_index,
> +			up->ctl_msg_buffer,
> +			datalen,
> +			UCAN_USB_CTL_PIPE_TIMEOUT);
> +}
> +
> +static int ucan_device_request_in(struct ucan_priv *up,
> +				  u8 cmd,
> +				  u16 subcmd,
> +				  size_t datalen)
> +{
> +	if (datalen > sizeof(union ucan_ctl_payload))
> +		return -ENOMEM;
> +
> +	return usb_control_msg(up->udev,
> +			usb_rcvctrlpipe(up->udev, 0),
> +			cmd,
> +			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			cpu_to_le16(subcmd),
> +			0,
> +			up->ctl_msg_buffer,
> +			datalen,
> +			UCAN_USB_CTL_PIPE_TIMEOUT);
> +}
> +
> +/* Parse the device information structure reported by the device and
> + * setup private variables accordingly
> + */
> +static void ucan_parse_device_info(struct ucan_priv *up,
> +				   struct ucan_ctl_cmd_device_info
> +					*ctl_cmd_device_info)
> +{
> +	struct can_bittiming_const *bittiming =
> +		&up->device_info.bittiming_const;
> +	u16 ctrlmodes;
> +
> +	/* store the data */
> +	up->can.clock.freq = le32_to_cpu(ctl_cmd_device_info->freq);
> +	up->device_info.tx_fifo = ctl_cmd_device_info->tx_fifo;
> +	strcpy(bittiming->name, "ucan");
> +	bittiming->tseg1_min = ctl_cmd_device_info->tseg1_min;
> +	bittiming->tseg1_max = ctl_cmd_device_info->tseg1_max;
> +	bittiming->tseg2_min = ctl_cmd_device_info->tseg2_min;
> +	bittiming->tseg2_max = ctl_cmd_device_info->tseg2_max;
> +	bittiming->sjw_max = ctl_cmd_device_info->sjw_max;
> +	bittiming->brp_min = le32_to_cpu(ctl_cmd_device_info->brp_min);
> +	bittiming->brp_max = le32_to_cpu(ctl_cmd_device_info->brp_max);
> +	bittiming->brp_inc = le16_to_cpu(ctl_cmd_device_info->brp_inc);
> +
> +	ctrlmodes = le16_to_cpu(ctl_cmd_device_info->ctrlmodes);
> +
> +	up->can.ctrlmode_supported = 0;
> +
> +	if (ctrlmodes & UCAN_MODE_LOOPBACK)
> +		up->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK;
> +	if (ctrlmodes & UCAN_MODE_SILENT)
> +		up->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> +	if (ctrlmodes & UCAN_MODE_3_SAMPLES)
> +		up->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
> +	if (ctrlmodes & UCAN_MODE_ONE_SHOT)
> +		up->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT;
> +	if (ctrlmodes & UCAN_MODE_BERR_REPORT)
> +		up->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING;
> +}
> +
> +/* Handle a CAN error frame that we have received from the device */
> +static void ucan_handle_error_frame(struct ucan_priv *up,
> +				    struct ucan_message_in *m,
> +				    u32 canid)

canid_t?

> +{
> +	enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
> +	struct net_device_stats *net_stats = &up->netdev->stats;
> +	struct can_device_stats *can_stats = &up->can.can_stats;
> +
> +	if (canid & CAN_ERR_LOSTARB)
> +		can_stats->arbitration_lost++;
> +
> +	if (canid & CAN_ERR_BUSERROR)
> +		can_stats->bus_error++;
> +
> +	if (canid & CAN_ERR_ACK)
> +		net_stats->tx_errors++;
> +
> +	if (canid & CAN_ERR_BUSOFF)
> +		new_state = CAN_STATE_BUS_OFF;
> +
> +	/* controller problems, details in data[1] */
> +	if (canid & CAN_ERR_CRTL) {
> +		u8 d1 = m->msg.can_msg.data[1];
> +
> +		if (d1 & (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE))
> +			new_state = max(new_state, (enum can_state)
> +					CAN_STATE_ERROR_PASSIVE);
> +
> +		if (d1 & (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING))
> +			new_state = max(new_state, (enum can_state)
> +					CAN_STATE_ERROR_WARNING);
> +
> +		if (d1 & CAN_ERR_CRTL_RX_OVERFLOW)
> +			net_stats->rx_over_errors++;
> +	}
> +
> +	/* protocol error, details in data[2] */
> +	if (canid & CAN_ERR_PROT) {
> +		u8 d2 = m->msg.can_msg.data[2];
> +
> +		if (d2 & CAN_ERR_PROT_TX)
> +			net_stats->tx_errors++;
> +		else
> +			net_stats->rx_errors++;
> +	}
> +
> +	/* we switched into a better state */
> +	if (up->can.state >= new_state) {
> +		up->can.state = new_state;
> +		return;
> +	}
> +
> +	/* we switched into a worse state */
> +	up->can.state = new_state;
> +	switch (new_state) {
> +	case CAN_STATE_BUS_OFF:
> +		can_stats->bus_off++;
> +		can_bus_off(up->netdev);
> +		netdev_info(up->netdev,
> +			    "link has gone into BUS-OFF state\n");
> +		break;
> +	case CAN_STATE_ERROR_PASSIVE:
> +		can_stats->error_passive++;
> +		break;
> +	case CAN_STATE_ERROR_WARNING:
> +		can_stats->error_warning++;
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +/* Callback on reception of a can frame via the IN endpoint
> + *
> + * This function allocates an skb and transferres it to the Linux
> + * network stack
> + */
> +static void ucan_rx_can_msg(struct ucan_priv *up, struct ucan_message_in *m)
> +{
> +	int len;
> +	u32 canid;

canid_t

> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +	struct net_device_stats *stats = &up->netdev->stats;
> +
> +	/* get the contents of the length field */
> +	len = le16_to_cpu(m->len);
> +
> +	/* check sanity */
> +	if (len < UCAN_IN_HDR_SIZE + sizeof(m->msg.can_msg.id)) {
> +		dev_warn(&up->udev->dev, "invalid input message len\n");
> +		return;
> +	}
> +
> +	/* handle error frames */
> +	canid = le32_to_cpu(m->msg.can_msg.id);
> +	if (canid & CAN_ERR_FLAG) {
> +		ucan_handle_error_frame(up, m, canid);
> +		/* drop frame if berr-reporting is off */
> +		if (!(up->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
> +			return;
> +	}
> +
> +	/* allocate skb */
> +	skb = alloc_can_skb(up->netdev, &cf);
> +	if (!skb)
> +		return;
> +
> +	/* fill the can frame */
> +	cf->can_id = le32_to_cpu(m->msg.can_msg.id);

= canid;

> +
> +	/* compute DLC taking RTR_FLAG into account */
> +	cf->can_dlc = ucan_compute_dlc(len, &m->msg.can_msg);
> +
> +	if (cf->can_dlc > CAN_MAX_DLEN) {

Just get_can_dlc();

> +		dev_warn(&up->udev->dev,
> +			 "dropping CAN frame due to DLC field\n");
> +		goto err_freeskb;
> +	}
> +
> +	if (cf->can_id & CAN_EFF_FLAG)
> +		cf->can_id &=
> +		    (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_ERR_FLAG);
> +	else
> +		cf->can_id &= (CAN_SFF_MASK | CAN_RTR_FLAG | CAN_ERR_FLAG);

What about moving the common flags in front of the if()?

> +
> +	if ((cf->can_id & CAN_RTR_FLAG) != CAN_RTR_FLAG)

if (cf->can_id & CAN_RTR_FLAG)

should be enough

> +		memcpy(cf->data, m->msg.can_msg.data, cf->can_dlc);
> +
> +	/* don't count error frames as real packets */
> +	if (!(canid & CAN_ERR_FLAG)) {
> +		stats->rx_packets++;
> +		stats->rx_bytes += cf->can_dlc;
> +	}

Please count them, too.

> +
> +	/* pass it to Linux */
> +	netif_rx(skb);
> +
> +	return;
> +err_freeskb:
> +	kfree_skb(skb);
> +}
> +
> +/* callback indicating completed transmission */
> +static void ucan_tx_complete_msg(struct ucan_priv *up,
> +				 struct ucan_message_in *m)
> +{
> +	u16 count, i;
> +	u8 echo_id;
> +	u16 len = le16_to_cpu(m->len);
> +
> +	struct ucan_urb_context *context;
> +
> +	if (len < UCAN_IN_HDR_SIZE || (len % 2 != 0)) {
> +		dev_dbg(&up->udev->dev, "%s Invalid tx complete length\n",
> +			__func__);

Can you handle packages with wrong length?

> +	}
> +
> +	count = (len - UCAN_IN_HDR_SIZE) / 2;
> +	for (i = 0; i < count; i++) {
> +		/* we did not submit such echo ids */
> +		echo_id = m->msg.can_tx_complete_msg[i].echo_id;
> +		if (echo_id >= up->device_info.tx_fifo) {
> +			up->netdev->stats.tx_errors++;
> +			dev_err(&up->udev->dev,
> +				"device answered with invalid echo_id\n");
> +			continue;
> +		}
> +
> +		context = &up->tx_contexts[echo_id];
> +		if (atomic_read(&context->allocated) == 0) {
> +			dev_err(&up->udev->dev,
> +				"device answered with unallocated echo id %d\n",
> +				echo_id);
> +			continue;
> +		}
> +
> +		if (m->msg.can_tx_complete_msg[i].flags &
> +		    UCAN_TX_COMPLETE_SUCCESS) {
> +			/* update statistics */
> +			up->netdev->stats.tx_packets++;
> +			up->netdev->stats.tx_bytes += context->dlc;
> +			can_get_echo_skb(up->netdev, context->echo_index);
> +		} else {
> +			up->netdev->stats.tx_dropped++;
> +			can_free_echo_skb(up->netdev, context->echo_index);
> +		}
> +
> +		/* Release context and restart queue if necessary */
> +		ucan_release_context(up, context);
> +	}
> +}
> +
> +/* callback on reception of a USB message */
> +static void ucan_read_bulk_callback(struct urb *urb)
> +{
> +	int ret;
> +	int pos;
> +	struct ucan_priv *up = urb->context;
> +	struct net_device *netdev = up->netdev;
> +	struct ucan_message_in *m;
> +
> +	/* the device is not up and the driver should not receive any
> +	 * data on the bulk in pipe
> +	 */
> +	WARN_ON(!up->tx_contexts);
> +	if (!up->tx_contexts) {

if (WARN_ON())

... I'll review the rest later.

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   |



Download attachment "signature.asc" of type "application/pgp-signature" (489 bytes)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ