xref: /linux/drivers/net/can/usb/ucan.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
19f2d3eaeSJakob Unterwurzacher // SPDX-License-Identifier: GPL-2.0
29f2d3eaeSJakob Unterwurzacher 
39f2d3eaeSJakob Unterwurzacher /* Driver for Theobroma Systems UCAN devices, Protocol Version 3
49f2d3eaeSJakob Unterwurzacher  *
59f2d3eaeSJakob Unterwurzacher  * Copyright (C) 2018 Theobroma Systems Design und Consulting GmbH
69f2d3eaeSJakob Unterwurzacher  *
79f2d3eaeSJakob Unterwurzacher  *
89f2d3eaeSJakob Unterwurzacher  * General Description:
99f2d3eaeSJakob Unterwurzacher  *
109f2d3eaeSJakob Unterwurzacher  * The USB Device uses three Endpoints:
119f2d3eaeSJakob Unterwurzacher  *
129f2d3eaeSJakob Unterwurzacher  *   CONTROL Endpoint: Is used the setup the device (start, stop,
139f2d3eaeSJakob Unterwurzacher  *   info, configure).
149f2d3eaeSJakob Unterwurzacher  *
159f2d3eaeSJakob Unterwurzacher  *   IN Endpoint: The device sends CAN Frame Messages and Device
169f2d3eaeSJakob Unterwurzacher  *   Information using the IN endpoint.
179f2d3eaeSJakob Unterwurzacher  *
189f2d3eaeSJakob Unterwurzacher  *   OUT Endpoint: The driver sends configuration requests, and CAN
199f2d3eaeSJakob Unterwurzacher  *   Frames on the out endpoint.
209f2d3eaeSJakob Unterwurzacher  *
219f2d3eaeSJakob Unterwurzacher  * Error Handling:
229f2d3eaeSJakob Unterwurzacher  *
239f2d3eaeSJakob Unterwurzacher  *   If error reporting is turned on the device encodes error into CAN
249f2d3eaeSJakob Unterwurzacher  *   error frames (see uapi/linux/can/error.h) and sends it using the
259f2d3eaeSJakob Unterwurzacher  *   IN Endpoint. The driver updates statistics and forward it.
269f2d3eaeSJakob Unterwurzacher  */
279f2d3eaeSJakob Unterwurzacher 
289f2d3eaeSJakob Unterwurzacher #include <linux/can.h>
299f2d3eaeSJakob Unterwurzacher #include <linux/can/dev.h>
309f2d3eaeSJakob Unterwurzacher #include <linux/can/error.h>
31409c188cSVincent Mailhol #include <linux/ethtool.h>
329f2d3eaeSJakob Unterwurzacher #include <linux/module.h>
339f2d3eaeSJakob Unterwurzacher #include <linux/netdevice.h>
349f2d3eaeSJakob Unterwurzacher #include <linux/signal.h>
359f2d3eaeSJakob Unterwurzacher #include <linux/skbuff.h>
369f2d3eaeSJakob Unterwurzacher #include <linux/slab.h>
379f2d3eaeSJakob Unterwurzacher #include <linux/usb.h>
389f2d3eaeSJakob Unterwurzacher 
399f2d3eaeSJakob Unterwurzacher #define UCAN_DRIVER_NAME "ucan"
409f2d3eaeSJakob Unterwurzacher #define UCAN_MAX_RX_URBS 8
419f2d3eaeSJakob Unterwurzacher /* the CAN controller needs a while to enable/disable the bus */
429f2d3eaeSJakob Unterwurzacher #define UCAN_USB_CTL_PIPE_TIMEOUT 1000
439f2d3eaeSJakob Unterwurzacher /* this driver currently supports protocol version 3 only */
449f2d3eaeSJakob Unterwurzacher #define UCAN_PROTOCOL_VERSION_MIN 3
459f2d3eaeSJakob Unterwurzacher #define UCAN_PROTOCOL_VERSION_MAX 3
469f2d3eaeSJakob Unterwurzacher 
479f2d3eaeSJakob Unterwurzacher /* UCAN Message Definitions
489f2d3eaeSJakob Unterwurzacher  * ------------------------
499f2d3eaeSJakob Unterwurzacher  *
509f2d3eaeSJakob Unterwurzacher  *  ucan_message_out_t and ucan_message_in_t define the messages
519f2d3eaeSJakob Unterwurzacher  *  transmitted on the OUT and IN endpoint.
529f2d3eaeSJakob Unterwurzacher  *
539f2d3eaeSJakob Unterwurzacher  *  Multibyte fields are transmitted with little endianness
549f2d3eaeSJakob Unterwurzacher  *
559f2d3eaeSJakob Unterwurzacher  *  INTR Endpoint: a single uint32_t storing the current space in the fifo
569f2d3eaeSJakob Unterwurzacher  *
579f2d3eaeSJakob Unterwurzacher  *  OUT Endpoint: single message of type ucan_message_out_t is
589f2d3eaeSJakob Unterwurzacher  *    transmitted on the out endpoint
599f2d3eaeSJakob Unterwurzacher  *
609f2d3eaeSJakob Unterwurzacher  *  IN Endpoint: multiple messages ucan_message_in_t concateted in
619f2d3eaeSJakob Unterwurzacher  *    the following way:
629f2d3eaeSJakob Unterwurzacher  *
639f2d3eaeSJakob Unterwurzacher  *	m[n].len <=> the length if message n(including the header in bytes)
649f2d3eaeSJakob Unterwurzacher  *	m[n] is is aligned to a 4 byte boundary, hence
659f2d3eaeSJakob Unterwurzacher  *	  offset(m[0])	 := 0;
669f2d3eaeSJakob Unterwurzacher  *	  offset(m[n+1]) := offset(m[n]) + (m[n].len + 3) & 3
679f2d3eaeSJakob Unterwurzacher  *
689f2d3eaeSJakob Unterwurzacher  *	this implies that
699f2d3eaeSJakob Unterwurzacher  *	  offset(m[n]) % 4 <=> 0
709f2d3eaeSJakob Unterwurzacher  */
719f2d3eaeSJakob Unterwurzacher 
729f2d3eaeSJakob Unterwurzacher /* Device Global Commands */
739f2d3eaeSJakob Unterwurzacher enum {
749f2d3eaeSJakob Unterwurzacher 	UCAN_DEVICE_GET_FW_STRING = 0,
759f2d3eaeSJakob Unterwurzacher };
769f2d3eaeSJakob Unterwurzacher 
779f2d3eaeSJakob Unterwurzacher /* UCAN Commands */
789f2d3eaeSJakob Unterwurzacher enum {
799f2d3eaeSJakob Unterwurzacher 	/* start the can transceiver - val defines the operation mode */
809f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_START = 0,
819f2d3eaeSJakob Unterwurzacher 	/* cancel pending transmissions and stop the can transceiver */
829f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_STOP = 1,
839f2d3eaeSJakob Unterwurzacher 	/* send can transceiver into low-power sleep mode */
849f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_SLEEP = 2,
859f2d3eaeSJakob Unterwurzacher 	/* wake up can transceiver from low-power sleep mode */
869f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_WAKEUP = 3,
879f2d3eaeSJakob Unterwurzacher 	/* reset the can transceiver */
889f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_RESET = 4,
899f2d3eaeSJakob Unterwurzacher 	/* get piece of info from the can transceiver - subcmd defines what
909f2d3eaeSJakob Unterwurzacher 	 * piece
919f2d3eaeSJakob Unterwurzacher 	 */
929f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_GET = 5,
939f2d3eaeSJakob Unterwurzacher 	/* clear or disable hardware filter - subcmd defines which of the two */
949f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_FILTER = 6,
959f2d3eaeSJakob Unterwurzacher 	/* Setup bittiming */
969f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_SET_BITTIMING = 7,
979f2d3eaeSJakob Unterwurzacher 	/* recover from bus-off state */
989f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_RESTART = 8,
999f2d3eaeSJakob Unterwurzacher };
1009f2d3eaeSJakob Unterwurzacher 
1019f2d3eaeSJakob Unterwurzacher /* UCAN_COMMAND_START and UCAN_COMMAND_GET_INFO operation modes (bitmap).
1029f2d3eaeSJakob Unterwurzacher  * Undefined bits must be set to 0.
1039f2d3eaeSJakob Unterwurzacher  */
1049f2d3eaeSJakob Unterwurzacher enum {
1059f2d3eaeSJakob Unterwurzacher 	UCAN_MODE_LOOPBACK = BIT(0),
1069f2d3eaeSJakob Unterwurzacher 	UCAN_MODE_SILENT = BIT(1),
1079f2d3eaeSJakob Unterwurzacher 	UCAN_MODE_3_SAMPLES = BIT(2),
1089f2d3eaeSJakob Unterwurzacher 	UCAN_MODE_ONE_SHOT = BIT(3),
1099f2d3eaeSJakob Unterwurzacher 	UCAN_MODE_BERR_REPORT = BIT(4),
1109f2d3eaeSJakob Unterwurzacher };
1119f2d3eaeSJakob Unterwurzacher 
1129f2d3eaeSJakob Unterwurzacher /* UCAN_COMMAND_GET subcommands */
1139f2d3eaeSJakob Unterwurzacher enum {
1149f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_GET_INFO = 0,
1159f2d3eaeSJakob Unterwurzacher 	UCAN_COMMAND_GET_PROTOCOL_VERSION = 1,
1169f2d3eaeSJakob Unterwurzacher };
1179f2d3eaeSJakob Unterwurzacher 
1189f2d3eaeSJakob Unterwurzacher /* UCAN_COMMAND_FILTER subcommands */
1199f2d3eaeSJakob Unterwurzacher enum {
1209f2d3eaeSJakob Unterwurzacher 	UCAN_FILTER_CLEAR = 0,
1219f2d3eaeSJakob Unterwurzacher 	UCAN_FILTER_DISABLE = 1,
1229f2d3eaeSJakob Unterwurzacher 	UCAN_FILTER_ENABLE = 2,
1239f2d3eaeSJakob Unterwurzacher };
1249f2d3eaeSJakob Unterwurzacher 
1259f2d3eaeSJakob Unterwurzacher /* OUT endpoint message types */
1269f2d3eaeSJakob Unterwurzacher enum {
1279f2d3eaeSJakob Unterwurzacher 	UCAN_OUT_TX = 2,     /* transmit a CAN frame */
1289f2d3eaeSJakob Unterwurzacher };
1299f2d3eaeSJakob Unterwurzacher 
1309f2d3eaeSJakob Unterwurzacher /* IN endpoint message types */
1319f2d3eaeSJakob Unterwurzacher enum {
1329f2d3eaeSJakob Unterwurzacher 	UCAN_IN_TX_COMPLETE = 1,  /* CAN frame transmission completed */
1339f2d3eaeSJakob Unterwurzacher 	UCAN_IN_RX = 2,           /* CAN frame received */
1349f2d3eaeSJakob Unterwurzacher };
1359f2d3eaeSJakob Unterwurzacher 
1369f2d3eaeSJakob Unterwurzacher struct ucan_ctl_cmd_start {
1379f2d3eaeSJakob Unterwurzacher 	__le16 mode;         /* OR-ing any of UCAN_MODE_* */
1389f2d3eaeSJakob Unterwurzacher } __packed;
1399f2d3eaeSJakob Unterwurzacher 
1409f2d3eaeSJakob Unterwurzacher struct ucan_ctl_cmd_set_bittiming {
1419f2d3eaeSJakob Unterwurzacher 	__le32 tq;           /* Time quanta (TQ) in nanoseconds */
1429f2d3eaeSJakob Unterwurzacher 	__le16 brp;          /* TQ Prescaler */
1439f2d3eaeSJakob Unterwurzacher 	__le16 sample_point; /* Samplepoint on tenth percent */
1449f2d3eaeSJakob Unterwurzacher 	u8 prop_seg;         /* Propagation segment in TQs */
1459f2d3eaeSJakob Unterwurzacher 	u8 phase_seg1;       /* Phase buffer segment 1 in TQs */
1469f2d3eaeSJakob Unterwurzacher 	u8 phase_seg2;       /* Phase buffer segment 2 in TQs */
1479f2d3eaeSJakob Unterwurzacher 	u8 sjw;              /* Synchronisation jump width in TQs */
1489f2d3eaeSJakob Unterwurzacher } __packed;
1499f2d3eaeSJakob Unterwurzacher 
1509f2d3eaeSJakob Unterwurzacher struct ucan_ctl_cmd_device_info {
1519f2d3eaeSJakob Unterwurzacher 	__le32 freq;         /* Clock Frequency for tq generation */
1529f2d3eaeSJakob Unterwurzacher 	u8 tx_fifo;          /* Size of the transmission fifo */
1539f2d3eaeSJakob Unterwurzacher 	u8 sjw_max;          /* can_bittiming fields... */
1549f2d3eaeSJakob Unterwurzacher 	u8 tseg1_min;
1559f2d3eaeSJakob Unterwurzacher 	u8 tseg1_max;
1569f2d3eaeSJakob Unterwurzacher 	u8 tseg2_min;
1579f2d3eaeSJakob Unterwurzacher 	u8 tseg2_max;
1589f2d3eaeSJakob Unterwurzacher 	__le16 brp_inc;
1599f2d3eaeSJakob Unterwurzacher 	__le32 brp_min;
1609f2d3eaeSJakob Unterwurzacher 	__le32 brp_max;      /* ...can_bittiming fields */
1619f2d3eaeSJakob Unterwurzacher 	__le16 ctrlmodes;    /* supported control modes */
1629f2d3eaeSJakob Unterwurzacher 	__le16 hwfilter;     /* Number of HW filter banks */
1639f2d3eaeSJakob Unterwurzacher 	__le16 rxmboxes;     /* Number of receive Mailboxes */
1649f2d3eaeSJakob Unterwurzacher } __packed;
1659f2d3eaeSJakob Unterwurzacher 
1669f2d3eaeSJakob Unterwurzacher struct ucan_ctl_cmd_get_protocol_version {
1679f2d3eaeSJakob Unterwurzacher 	__le32 version;
1689f2d3eaeSJakob Unterwurzacher } __packed;
1699f2d3eaeSJakob Unterwurzacher 
1709f2d3eaeSJakob Unterwurzacher union ucan_ctl_payload {
1719f2d3eaeSJakob Unterwurzacher 	/* Setup Bittiming
1729f2d3eaeSJakob Unterwurzacher 	 * bmRequest == UCAN_COMMAND_START
1739f2d3eaeSJakob Unterwurzacher 	 */
1749f2d3eaeSJakob Unterwurzacher 	struct ucan_ctl_cmd_start cmd_start;
1759f2d3eaeSJakob Unterwurzacher 	/* Setup Bittiming
1769f2d3eaeSJakob Unterwurzacher 	 * bmRequest == UCAN_COMMAND_SET_BITTIMING
1779f2d3eaeSJakob Unterwurzacher 	 */
1789f2d3eaeSJakob Unterwurzacher 	struct ucan_ctl_cmd_set_bittiming cmd_set_bittiming;
1799f2d3eaeSJakob Unterwurzacher 	/* Get Device Information
1809f2d3eaeSJakob Unterwurzacher 	 * bmRequest == UCAN_COMMAND_GET; wValue = UCAN_COMMAND_GET_INFO
1819f2d3eaeSJakob Unterwurzacher 	 */
1829f2d3eaeSJakob Unterwurzacher 	struct ucan_ctl_cmd_device_info cmd_get_device_info;
1839f2d3eaeSJakob Unterwurzacher 	/* Get Protocol Version
1849f2d3eaeSJakob Unterwurzacher 	 * bmRequest == UCAN_COMMAND_GET;
1859f2d3eaeSJakob Unterwurzacher 	 * wValue = UCAN_COMMAND_GET_PROTOCOL_VERSION
1869f2d3eaeSJakob Unterwurzacher 	 */
1879f2d3eaeSJakob Unterwurzacher 	struct ucan_ctl_cmd_get_protocol_version cmd_get_protocol_version;
1889f2d3eaeSJakob Unterwurzacher 
1899f2d3eaeSJakob Unterwurzacher 	u8 raw[128];
1909f2d3eaeSJakob Unterwurzacher } __packed;
1919f2d3eaeSJakob Unterwurzacher 
1929f2d3eaeSJakob Unterwurzacher enum {
1939f2d3eaeSJakob Unterwurzacher 	UCAN_TX_COMPLETE_SUCCESS = BIT(0),
1949f2d3eaeSJakob Unterwurzacher };
1959f2d3eaeSJakob Unterwurzacher 
1969f2d3eaeSJakob Unterwurzacher /* Transmission Complete within ucan_message_in */
1979f2d3eaeSJakob Unterwurzacher struct ucan_tx_complete_entry_t {
1989f2d3eaeSJakob Unterwurzacher 	u8 echo_index;
1999f2d3eaeSJakob Unterwurzacher 	u8 flags;
2009f2d3eaeSJakob Unterwurzacher } __packed __aligned(0x2);
2019f2d3eaeSJakob Unterwurzacher 
2029f2d3eaeSJakob Unterwurzacher /* CAN Data message format within ucan_message_in/out */
2039f2d3eaeSJakob Unterwurzacher struct ucan_can_msg {
2049f2d3eaeSJakob Unterwurzacher 	/* note DLC is computed by
2059f2d3eaeSJakob Unterwurzacher 	 *    msg.len - sizeof (msg.len)
2069f2d3eaeSJakob Unterwurzacher 	 *            - sizeof (msg.type)
2079f2d3eaeSJakob Unterwurzacher 	 *            - sizeof (msg.can_msg.id)
2089f2d3eaeSJakob Unterwurzacher 	 */
2099f2d3eaeSJakob Unterwurzacher 	__le32 id;
2109f2d3eaeSJakob Unterwurzacher 
2119f2d3eaeSJakob Unterwurzacher 	union {
2129f2d3eaeSJakob Unterwurzacher 		u8 data[CAN_MAX_DLEN];  /* Data of CAN frames */
2139f2d3eaeSJakob Unterwurzacher 		u8 dlc;                 /* RTR dlc */
2149f2d3eaeSJakob Unterwurzacher 	};
2159f2d3eaeSJakob Unterwurzacher } __packed;
2169f2d3eaeSJakob Unterwurzacher 
2179f2d3eaeSJakob Unterwurzacher /* OUT Endpoint, outbound messages */
2189f2d3eaeSJakob Unterwurzacher struct ucan_message_out {
2199f2d3eaeSJakob Unterwurzacher 	__le16 len; /* Length of the content include header */
2209f2d3eaeSJakob Unterwurzacher 	u8 type;    /* UCAN_OUT_TX and friends */
2219f2d3eaeSJakob Unterwurzacher 	u8 subtype; /* command sub type */
2229f2d3eaeSJakob Unterwurzacher 
2239f2d3eaeSJakob Unterwurzacher 	union {
2249f2d3eaeSJakob Unterwurzacher 		/* Transmit CAN frame
2259f2d3eaeSJakob Unterwurzacher 		 * (type == UCAN_TX) && ((msg.can_msg.id & CAN_RTR_FLAG) == 0)
2269f2d3eaeSJakob Unterwurzacher 		 * subtype stores the echo id
2279f2d3eaeSJakob Unterwurzacher 		 */
2289f2d3eaeSJakob Unterwurzacher 		struct ucan_can_msg can_msg;
2299f2d3eaeSJakob Unterwurzacher 	} msg;
2309f2d3eaeSJakob Unterwurzacher } __packed __aligned(0x4);
2319f2d3eaeSJakob Unterwurzacher 
2329f2d3eaeSJakob Unterwurzacher /* IN Endpoint, inbound messages */
2339f2d3eaeSJakob Unterwurzacher struct ucan_message_in {
2349f2d3eaeSJakob Unterwurzacher 	__le16 len; /* Length of the content include header */
2359f2d3eaeSJakob Unterwurzacher 	u8 type;    /* UCAN_IN_RX and friends */
2369f2d3eaeSJakob Unterwurzacher 	u8 subtype; /* command sub type */
2379f2d3eaeSJakob Unterwurzacher 
2389f2d3eaeSJakob Unterwurzacher 	union {
2399f2d3eaeSJakob Unterwurzacher 		/* CAN Frame received
2409f2d3eaeSJakob Unterwurzacher 		 * (type == UCAN_IN_RX)
2419f2d3eaeSJakob Unterwurzacher 		 * && ((msg.can_msg.id & CAN_RTR_FLAG) == 0)
2429f2d3eaeSJakob Unterwurzacher 		 */
2439f2d3eaeSJakob Unterwurzacher 		struct ucan_can_msg can_msg;
2449f2d3eaeSJakob Unterwurzacher 
2459f2d3eaeSJakob Unterwurzacher 		/* CAN transmission complete
2469f2d3eaeSJakob Unterwurzacher 		 * (type == UCAN_IN_TX_COMPLETE)
2479f2d3eaeSJakob Unterwurzacher 		 */
248b2df8a1bSGustavo A. R. Silva 		DECLARE_FLEX_ARRAY(struct ucan_tx_complete_entry_t,
249b2df8a1bSGustavo A. R. Silva 				   can_tx_complete_msg);
2509f2d3eaeSJakob Unterwurzacher 	} __aligned(0x4) msg;
25127868a8fSArnd Bergmann } __packed __aligned(0x4);
2529f2d3eaeSJakob Unterwurzacher 
2539f2d3eaeSJakob Unterwurzacher /* Macros to calculate message lengths */
2549f2d3eaeSJakob Unterwurzacher #define UCAN_OUT_HDR_SIZE offsetof(struct ucan_message_out, msg)
2559f2d3eaeSJakob Unterwurzacher 
2569f2d3eaeSJakob Unterwurzacher #define UCAN_IN_HDR_SIZE offsetof(struct ucan_message_in, msg)
2579f2d3eaeSJakob Unterwurzacher #define UCAN_IN_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member))
2589f2d3eaeSJakob Unterwurzacher 
2599f2d3eaeSJakob Unterwurzacher struct ucan_priv;
2609f2d3eaeSJakob Unterwurzacher 
2619f2d3eaeSJakob Unterwurzacher /* Context Information for transmission URBs */
2629f2d3eaeSJakob Unterwurzacher struct ucan_urb_context {
2639f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up;
2649f2d3eaeSJakob Unterwurzacher 	bool allocated;
2659f2d3eaeSJakob Unterwurzacher };
2669f2d3eaeSJakob Unterwurzacher 
2679f2d3eaeSJakob Unterwurzacher /* Information reported by the USB device */
2689f2d3eaeSJakob Unterwurzacher struct ucan_device_info {
2699f2d3eaeSJakob Unterwurzacher 	struct can_bittiming_const bittiming_const;
2709f2d3eaeSJakob Unterwurzacher 	u8 tx_fifo;
2719f2d3eaeSJakob Unterwurzacher };
2729f2d3eaeSJakob Unterwurzacher 
2739f2d3eaeSJakob Unterwurzacher /* Driver private data */
2749f2d3eaeSJakob Unterwurzacher struct ucan_priv {
2759f2d3eaeSJakob Unterwurzacher 	/* must be the first member */
2769f2d3eaeSJakob Unterwurzacher 	struct can_priv can;
2779f2d3eaeSJakob Unterwurzacher 
2789f2d3eaeSJakob Unterwurzacher 	/* linux USB device structures */
2799f2d3eaeSJakob Unterwurzacher 	struct usb_device *udev;
2809f2d3eaeSJakob Unterwurzacher 	struct net_device *netdev;
2819f2d3eaeSJakob Unterwurzacher 
2829f2d3eaeSJakob Unterwurzacher 	/* lock for can->echo_skb (used around
2839f2d3eaeSJakob Unterwurzacher 	 * can_put/get/free_echo_skb
2849f2d3eaeSJakob Unterwurzacher 	 */
2859f2d3eaeSJakob Unterwurzacher 	spinlock_t echo_skb_lock;
2869f2d3eaeSJakob Unterwurzacher 
287*03df47c1SMao Zhu 	/* usb device information */
2889f2d3eaeSJakob Unterwurzacher 	u8 intf_index;
2899f2d3eaeSJakob Unterwurzacher 	u8 in_ep_addr;
2909f2d3eaeSJakob Unterwurzacher 	u8 out_ep_addr;
2919f2d3eaeSJakob Unterwurzacher 	u16 in_ep_size;
2929f2d3eaeSJakob Unterwurzacher 
2939f2d3eaeSJakob Unterwurzacher 	/* transmission and reception buffers */
2949f2d3eaeSJakob Unterwurzacher 	struct usb_anchor rx_urbs;
2959f2d3eaeSJakob Unterwurzacher 	struct usb_anchor tx_urbs;
2969f2d3eaeSJakob Unterwurzacher 
2979f2d3eaeSJakob Unterwurzacher 	union ucan_ctl_payload *ctl_msg_buffer;
2989f2d3eaeSJakob Unterwurzacher 	struct ucan_device_info device_info;
2999f2d3eaeSJakob Unterwurzacher 
3009f2d3eaeSJakob Unterwurzacher 	/* transmission control information and locks */
3019f2d3eaeSJakob Unterwurzacher 	spinlock_t context_lock;
3029f2d3eaeSJakob Unterwurzacher 	unsigned int available_tx_urbs;
3039f2d3eaeSJakob Unterwurzacher 	struct ucan_urb_context *context_array;
3049f2d3eaeSJakob Unterwurzacher };
3059f2d3eaeSJakob Unterwurzacher 
ucan_can_cc_dlc2len(struct ucan_can_msg * msg,u16 len)30669d98969SOliver Hartkopp static u8 ucan_can_cc_dlc2len(struct ucan_can_msg *msg, u16 len)
3079f2d3eaeSJakob Unterwurzacher {
3089f2d3eaeSJakob Unterwurzacher 	if (le32_to_cpu(msg->id) & CAN_RTR_FLAG)
30969d98969SOliver Hartkopp 		return can_cc_dlc2len(msg->dlc);
3109f2d3eaeSJakob Unterwurzacher 	else
31169d98969SOliver Hartkopp 		return can_cc_dlc2len(len - (UCAN_IN_HDR_SIZE + sizeof(msg->id)));
3129f2d3eaeSJakob Unterwurzacher }
3139f2d3eaeSJakob Unterwurzacher 
ucan_release_context_array(struct ucan_priv * up)3149f2d3eaeSJakob Unterwurzacher static void ucan_release_context_array(struct ucan_priv *up)
3159f2d3eaeSJakob Unterwurzacher {
3169f2d3eaeSJakob Unterwurzacher 	if (!up->context_array)
3179f2d3eaeSJakob Unterwurzacher 		return;
3189f2d3eaeSJakob Unterwurzacher 
3199f2d3eaeSJakob Unterwurzacher 	/* lock is not needed because, driver is currently opening or closing */
3209f2d3eaeSJakob Unterwurzacher 	up->available_tx_urbs = 0;
3219f2d3eaeSJakob Unterwurzacher 
3229f2d3eaeSJakob Unterwurzacher 	kfree(up->context_array);
3239f2d3eaeSJakob Unterwurzacher 	up->context_array = NULL;
3249f2d3eaeSJakob Unterwurzacher }
3259f2d3eaeSJakob Unterwurzacher 
ucan_alloc_context_array(struct ucan_priv * up)3269f2d3eaeSJakob Unterwurzacher static int ucan_alloc_context_array(struct ucan_priv *up)
3279f2d3eaeSJakob Unterwurzacher {
3289f2d3eaeSJakob Unterwurzacher 	int i;
3299f2d3eaeSJakob Unterwurzacher 
3309f2d3eaeSJakob Unterwurzacher 	/* release contexts if any */
3319f2d3eaeSJakob Unterwurzacher 	ucan_release_context_array(up);
3329f2d3eaeSJakob Unterwurzacher 
3339f2d3eaeSJakob Unterwurzacher 	up->context_array = kcalloc(up->device_info.tx_fifo,
3349f2d3eaeSJakob Unterwurzacher 				    sizeof(*up->context_array),
3359f2d3eaeSJakob Unterwurzacher 				    GFP_KERNEL);
3369f2d3eaeSJakob Unterwurzacher 	if (!up->context_array) {
3379f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev,
3389f2d3eaeSJakob Unterwurzacher 			   "Not enough memory to allocate tx contexts\n");
3399f2d3eaeSJakob Unterwurzacher 		return -ENOMEM;
3409f2d3eaeSJakob Unterwurzacher 	}
3419f2d3eaeSJakob Unterwurzacher 
3429f2d3eaeSJakob Unterwurzacher 	for (i = 0; i < up->device_info.tx_fifo; i++) {
3439f2d3eaeSJakob Unterwurzacher 		up->context_array[i].allocated = false;
3449f2d3eaeSJakob Unterwurzacher 		up->context_array[i].up = up;
3459f2d3eaeSJakob Unterwurzacher 	}
3469f2d3eaeSJakob Unterwurzacher 
3479f2d3eaeSJakob Unterwurzacher 	/* lock is not needed because, driver is currently opening */
3489f2d3eaeSJakob Unterwurzacher 	up->available_tx_urbs = up->device_info.tx_fifo;
3499f2d3eaeSJakob Unterwurzacher 
3509f2d3eaeSJakob Unterwurzacher 	return 0;
3519f2d3eaeSJakob Unterwurzacher }
3529f2d3eaeSJakob Unterwurzacher 
ucan_alloc_context(struct ucan_priv * up)3539f2d3eaeSJakob Unterwurzacher static struct ucan_urb_context *ucan_alloc_context(struct ucan_priv *up)
3549f2d3eaeSJakob Unterwurzacher {
3559f2d3eaeSJakob Unterwurzacher 	int i;
3569f2d3eaeSJakob Unterwurzacher 	unsigned long flags;
3579f2d3eaeSJakob Unterwurzacher 	struct ucan_urb_context *ret = NULL;
3589f2d3eaeSJakob Unterwurzacher 
3599f2d3eaeSJakob Unterwurzacher 	if (WARN_ON_ONCE(!up->context_array))
3609f2d3eaeSJakob Unterwurzacher 		return NULL;
3619f2d3eaeSJakob Unterwurzacher 
3629f2d3eaeSJakob Unterwurzacher 	/* execute context operation atomically */
3639f2d3eaeSJakob Unterwurzacher 	spin_lock_irqsave(&up->context_lock, flags);
3649f2d3eaeSJakob Unterwurzacher 
3659f2d3eaeSJakob Unterwurzacher 	for (i = 0; i < up->device_info.tx_fifo; i++) {
3669f2d3eaeSJakob Unterwurzacher 		if (!up->context_array[i].allocated) {
3679f2d3eaeSJakob Unterwurzacher 			/* update context */
3689f2d3eaeSJakob Unterwurzacher 			ret = &up->context_array[i];
3699f2d3eaeSJakob Unterwurzacher 			up->context_array[i].allocated = true;
3709f2d3eaeSJakob Unterwurzacher 
3719f2d3eaeSJakob Unterwurzacher 			/* stop queue if necessary */
3729f2d3eaeSJakob Unterwurzacher 			up->available_tx_urbs--;
3739f2d3eaeSJakob Unterwurzacher 			if (!up->available_tx_urbs)
3749f2d3eaeSJakob Unterwurzacher 				netif_stop_queue(up->netdev);
3759f2d3eaeSJakob Unterwurzacher 
3769f2d3eaeSJakob Unterwurzacher 			break;
3779f2d3eaeSJakob Unterwurzacher 		}
3789f2d3eaeSJakob Unterwurzacher 	}
3799f2d3eaeSJakob Unterwurzacher 
3809f2d3eaeSJakob Unterwurzacher 	spin_unlock_irqrestore(&up->context_lock, flags);
3819f2d3eaeSJakob Unterwurzacher 	return ret;
3829f2d3eaeSJakob Unterwurzacher }
3839f2d3eaeSJakob Unterwurzacher 
ucan_release_context(struct ucan_priv * up,struct ucan_urb_context * ctx)3849f2d3eaeSJakob Unterwurzacher static bool ucan_release_context(struct ucan_priv *up,
3859f2d3eaeSJakob Unterwurzacher 				 struct ucan_urb_context *ctx)
3869f2d3eaeSJakob Unterwurzacher {
3879f2d3eaeSJakob Unterwurzacher 	unsigned long flags;
3889f2d3eaeSJakob Unterwurzacher 	bool ret = false;
3899f2d3eaeSJakob Unterwurzacher 
3909f2d3eaeSJakob Unterwurzacher 	if (WARN_ON_ONCE(!up->context_array))
3919f2d3eaeSJakob Unterwurzacher 		return false;
3929f2d3eaeSJakob Unterwurzacher 
3939f2d3eaeSJakob Unterwurzacher 	/* execute context operation atomically */
3949f2d3eaeSJakob Unterwurzacher 	spin_lock_irqsave(&up->context_lock, flags);
3959f2d3eaeSJakob Unterwurzacher 
3969f2d3eaeSJakob Unterwurzacher 	/* context was not allocated, maybe the device sent garbage */
3979f2d3eaeSJakob Unterwurzacher 	if (ctx->allocated) {
3989f2d3eaeSJakob Unterwurzacher 		ctx->allocated = false;
3999f2d3eaeSJakob Unterwurzacher 
4009f2d3eaeSJakob Unterwurzacher 		/* check if the queue needs to be woken */
4019f2d3eaeSJakob Unterwurzacher 		if (!up->available_tx_urbs)
4029f2d3eaeSJakob Unterwurzacher 			netif_wake_queue(up->netdev);
4039f2d3eaeSJakob Unterwurzacher 		up->available_tx_urbs++;
4049f2d3eaeSJakob Unterwurzacher 
4059f2d3eaeSJakob Unterwurzacher 		ret = true;
4069f2d3eaeSJakob Unterwurzacher 	}
4079f2d3eaeSJakob Unterwurzacher 
4089f2d3eaeSJakob Unterwurzacher 	spin_unlock_irqrestore(&up->context_lock, flags);
4099f2d3eaeSJakob Unterwurzacher 	return ret;
4109f2d3eaeSJakob Unterwurzacher }
4119f2d3eaeSJakob Unterwurzacher 
ucan_ctrl_command_out(struct ucan_priv * up,u8 cmd,u16 subcmd,u16 datalen)4129f2d3eaeSJakob Unterwurzacher static int ucan_ctrl_command_out(struct ucan_priv *up,
4139f2d3eaeSJakob Unterwurzacher 				 u8 cmd, u16 subcmd, u16 datalen)
4149f2d3eaeSJakob Unterwurzacher {
4159f2d3eaeSJakob Unterwurzacher 	return usb_control_msg(up->udev,
4169f2d3eaeSJakob Unterwurzacher 			       usb_sndctrlpipe(up->udev, 0),
4179f2d3eaeSJakob Unterwurzacher 			       cmd,
4189f2d3eaeSJakob Unterwurzacher 			       USB_DIR_OUT | USB_TYPE_VENDOR |
4199f2d3eaeSJakob Unterwurzacher 						USB_RECIP_INTERFACE,
4209f2d3eaeSJakob Unterwurzacher 			       subcmd,
4219f2d3eaeSJakob Unterwurzacher 			       up->intf_index,
4229f2d3eaeSJakob Unterwurzacher 			       up->ctl_msg_buffer,
4239f2d3eaeSJakob Unterwurzacher 			       datalen,
4249f2d3eaeSJakob Unterwurzacher 			       UCAN_USB_CTL_PIPE_TIMEOUT);
4259f2d3eaeSJakob Unterwurzacher }
4269f2d3eaeSJakob Unterwurzacher 
ucan_device_request_in(struct ucan_priv * up,u8 cmd,u16 subcmd,u16 datalen)4279f2d3eaeSJakob Unterwurzacher static int ucan_device_request_in(struct ucan_priv *up,
4289f2d3eaeSJakob Unterwurzacher 				  u8 cmd, u16 subcmd, u16 datalen)
4299f2d3eaeSJakob Unterwurzacher {
4309f2d3eaeSJakob Unterwurzacher 	return usb_control_msg(up->udev,
4319f2d3eaeSJakob Unterwurzacher 			       usb_rcvctrlpipe(up->udev, 0),
4329f2d3eaeSJakob Unterwurzacher 			       cmd,
4339f2d3eaeSJakob Unterwurzacher 			       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
4349f2d3eaeSJakob Unterwurzacher 			       subcmd,
4359f2d3eaeSJakob Unterwurzacher 			       0,
4369f2d3eaeSJakob Unterwurzacher 			       up->ctl_msg_buffer,
4379f2d3eaeSJakob Unterwurzacher 			       datalen,
4389f2d3eaeSJakob Unterwurzacher 			       UCAN_USB_CTL_PIPE_TIMEOUT);
4399f2d3eaeSJakob Unterwurzacher }
4409f2d3eaeSJakob Unterwurzacher 
4419f2d3eaeSJakob Unterwurzacher /* Parse the device information structure reported by the device and
4429f2d3eaeSJakob Unterwurzacher  * setup private variables accordingly
4439f2d3eaeSJakob Unterwurzacher  */
ucan_parse_device_info(struct ucan_priv * up,struct ucan_ctl_cmd_device_info * device_info)4449f2d3eaeSJakob Unterwurzacher static void ucan_parse_device_info(struct ucan_priv *up,
4459f2d3eaeSJakob Unterwurzacher 				   struct ucan_ctl_cmd_device_info *device_info)
4469f2d3eaeSJakob Unterwurzacher {
4479f2d3eaeSJakob Unterwurzacher 	struct can_bittiming_const *bittiming =
4489f2d3eaeSJakob Unterwurzacher 		&up->device_info.bittiming_const;
4499f2d3eaeSJakob Unterwurzacher 	u16 ctrlmodes;
4509f2d3eaeSJakob Unterwurzacher 
4519f2d3eaeSJakob Unterwurzacher 	/* store the data */
4529f2d3eaeSJakob Unterwurzacher 	up->can.clock.freq = le32_to_cpu(device_info->freq);
4539f2d3eaeSJakob Unterwurzacher 	up->device_info.tx_fifo = device_info->tx_fifo;
4549f2d3eaeSJakob Unterwurzacher 	strcpy(bittiming->name, "ucan");
4559f2d3eaeSJakob Unterwurzacher 	bittiming->tseg1_min = device_info->tseg1_min;
4569f2d3eaeSJakob Unterwurzacher 	bittiming->tseg1_max = device_info->tseg1_max;
4579f2d3eaeSJakob Unterwurzacher 	bittiming->tseg2_min = device_info->tseg2_min;
4589f2d3eaeSJakob Unterwurzacher 	bittiming->tseg2_max = device_info->tseg2_max;
4599f2d3eaeSJakob Unterwurzacher 	bittiming->sjw_max = device_info->sjw_max;
4609f2d3eaeSJakob Unterwurzacher 	bittiming->brp_min = le32_to_cpu(device_info->brp_min);
4619f2d3eaeSJakob Unterwurzacher 	bittiming->brp_max = le32_to_cpu(device_info->brp_max);
4629f2d3eaeSJakob Unterwurzacher 	bittiming->brp_inc = le16_to_cpu(device_info->brp_inc);
4639f2d3eaeSJakob Unterwurzacher 
4649f2d3eaeSJakob Unterwurzacher 	ctrlmodes = le16_to_cpu(device_info->ctrlmodes);
4659f2d3eaeSJakob Unterwurzacher 
4669f2d3eaeSJakob Unterwurzacher 	up->can.ctrlmode_supported = 0;
4679f2d3eaeSJakob Unterwurzacher 
4689f2d3eaeSJakob Unterwurzacher 	if (ctrlmodes & UCAN_MODE_LOOPBACK)
4699f2d3eaeSJakob Unterwurzacher 		up->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK;
4709f2d3eaeSJakob Unterwurzacher 	if (ctrlmodes & UCAN_MODE_SILENT)
4719f2d3eaeSJakob Unterwurzacher 		up->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
4729f2d3eaeSJakob Unterwurzacher 	if (ctrlmodes & UCAN_MODE_3_SAMPLES)
4739f2d3eaeSJakob Unterwurzacher 		up->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
4749f2d3eaeSJakob Unterwurzacher 	if (ctrlmodes & UCAN_MODE_ONE_SHOT)
4759f2d3eaeSJakob Unterwurzacher 		up->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT;
4769f2d3eaeSJakob Unterwurzacher 	if (ctrlmodes & UCAN_MODE_BERR_REPORT)
4779f2d3eaeSJakob Unterwurzacher 		up->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING;
4789f2d3eaeSJakob Unterwurzacher }
4799f2d3eaeSJakob Unterwurzacher 
4809f2d3eaeSJakob Unterwurzacher /* Handle a CAN error frame that we have received from the device.
4819f2d3eaeSJakob Unterwurzacher  * Returns true if the can state has changed.
4829f2d3eaeSJakob Unterwurzacher  */
ucan_handle_error_frame(struct ucan_priv * up,struct ucan_message_in * m,canid_t canid)4839f2d3eaeSJakob Unterwurzacher static bool ucan_handle_error_frame(struct ucan_priv *up,
4849f2d3eaeSJakob Unterwurzacher 				    struct ucan_message_in *m,
4859f2d3eaeSJakob Unterwurzacher 				    canid_t canid)
4869f2d3eaeSJakob Unterwurzacher {
4879f2d3eaeSJakob Unterwurzacher 	enum can_state new_state = up->can.state;
4889f2d3eaeSJakob Unterwurzacher 	struct net_device_stats *net_stats = &up->netdev->stats;
4899f2d3eaeSJakob Unterwurzacher 	struct can_device_stats *can_stats = &up->can.can_stats;
4909f2d3eaeSJakob Unterwurzacher 
4919f2d3eaeSJakob Unterwurzacher 	if (canid & CAN_ERR_LOSTARB)
4929f2d3eaeSJakob Unterwurzacher 		can_stats->arbitration_lost++;
4939f2d3eaeSJakob Unterwurzacher 
4949f2d3eaeSJakob Unterwurzacher 	if (canid & CAN_ERR_BUSERROR)
4959f2d3eaeSJakob Unterwurzacher 		can_stats->bus_error++;
4969f2d3eaeSJakob Unterwurzacher 
4979f2d3eaeSJakob Unterwurzacher 	if (canid & CAN_ERR_ACK)
4989f2d3eaeSJakob Unterwurzacher 		net_stats->tx_errors++;
4999f2d3eaeSJakob Unterwurzacher 
5009f2d3eaeSJakob Unterwurzacher 	if (canid & CAN_ERR_BUSOFF)
5019f2d3eaeSJakob Unterwurzacher 		new_state = CAN_STATE_BUS_OFF;
5029f2d3eaeSJakob Unterwurzacher 
5039f2d3eaeSJakob Unterwurzacher 	/* controller problems, details in data[1] */
5049f2d3eaeSJakob Unterwurzacher 	if (canid & CAN_ERR_CRTL) {
5059f2d3eaeSJakob Unterwurzacher 		u8 d1 = m->msg.can_msg.data[1];
5069f2d3eaeSJakob Unterwurzacher 
5079f2d3eaeSJakob Unterwurzacher 		if (d1 & CAN_ERR_CRTL_RX_OVERFLOW)
5089f2d3eaeSJakob Unterwurzacher 			net_stats->rx_over_errors++;
5099f2d3eaeSJakob Unterwurzacher 
5109f2d3eaeSJakob Unterwurzacher 		/* controller state bits: if multiple are set the worst wins */
5119f2d3eaeSJakob Unterwurzacher 		if (d1 & CAN_ERR_CRTL_ACTIVE)
5129f2d3eaeSJakob Unterwurzacher 			new_state = CAN_STATE_ERROR_ACTIVE;
5139f2d3eaeSJakob Unterwurzacher 
5149f2d3eaeSJakob Unterwurzacher 		if (d1 & (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING))
5159f2d3eaeSJakob Unterwurzacher 			new_state = CAN_STATE_ERROR_WARNING;
5169f2d3eaeSJakob Unterwurzacher 
5179f2d3eaeSJakob Unterwurzacher 		if (d1 & (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE))
5189f2d3eaeSJakob Unterwurzacher 			new_state = CAN_STATE_ERROR_PASSIVE;
5199f2d3eaeSJakob Unterwurzacher 	}
5209f2d3eaeSJakob Unterwurzacher 
5219f2d3eaeSJakob Unterwurzacher 	/* protocol error, details in data[2] */
5229f2d3eaeSJakob Unterwurzacher 	if (canid & CAN_ERR_PROT) {
5239f2d3eaeSJakob Unterwurzacher 		u8 d2 = m->msg.can_msg.data[2];
5249f2d3eaeSJakob Unterwurzacher 
5259f2d3eaeSJakob Unterwurzacher 		if (d2 & CAN_ERR_PROT_TX)
5269f2d3eaeSJakob Unterwurzacher 			net_stats->tx_errors++;
5279f2d3eaeSJakob Unterwurzacher 		else
5289f2d3eaeSJakob Unterwurzacher 			net_stats->rx_errors++;
5299f2d3eaeSJakob Unterwurzacher 	}
5309f2d3eaeSJakob Unterwurzacher 
5319f2d3eaeSJakob Unterwurzacher 	/* no state change - we are done */
5329f2d3eaeSJakob Unterwurzacher 	if (up->can.state == new_state)
5339f2d3eaeSJakob Unterwurzacher 		return false;
5349f2d3eaeSJakob Unterwurzacher 
5359f2d3eaeSJakob Unterwurzacher 	/* we switched into a better state */
5369f2d3eaeSJakob Unterwurzacher 	if (up->can.state > new_state) {
5379f2d3eaeSJakob Unterwurzacher 		up->can.state = new_state;
5389f2d3eaeSJakob Unterwurzacher 		return true;
5399f2d3eaeSJakob Unterwurzacher 	}
5409f2d3eaeSJakob Unterwurzacher 
5419f2d3eaeSJakob Unterwurzacher 	/* we switched into a worse state */
5429f2d3eaeSJakob Unterwurzacher 	up->can.state = new_state;
5439f2d3eaeSJakob Unterwurzacher 	switch (new_state) {
5449f2d3eaeSJakob Unterwurzacher 	case CAN_STATE_BUS_OFF:
5459f2d3eaeSJakob Unterwurzacher 		can_stats->bus_off++;
5469f2d3eaeSJakob Unterwurzacher 		can_bus_off(up->netdev);
5479f2d3eaeSJakob Unterwurzacher 		break;
5489f2d3eaeSJakob Unterwurzacher 	case CAN_STATE_ERROR_PASSIVE:
5499f2d3eaeSJakob Unterwurzacher 		can_stats->error_passive++;
5509f2d3eaeSJakob Unterwurzacher 		break;
5519f2d3eaeSJakob Unterwurzacher 	case CAN_STATE_ERROR_WARNING:
5529f2d3eaeSJakob Unterwurzacher 		can_stats->error_warning++;
5539f2d3eaeSJakob Unterwurzacher 		break;
5549f2d3eaeSJakob Unterwurzacher 	default:
5559f2d3eaeSJakob Unterwurzacher 		break;
5569f2d3eaeSJakob Unterwurzacher 	}
5579f2d3eaeSJakob Unterwurzacher 	return true;
5589f2d3eaeSJakob Unterwurzacher }
5599f2d3eaeSJakob Unterwurzacher 
5609f2d3eaeSJakob Unterwurzacher /* Callback on reception of a can frame via the IN endpoint
5619f2d3eaeSJakob Unterwurzacher  *
5629f2d3eaeSJakob Unterwurzacher  * This function allocates an skb and transferres it to the Linux
5639f2d3eaeSJakob Unterwurzacher  * network stack
5649f2d3eaeSJakob Unterwurzacher  */
ucan_rx_can_msg(struct ucan_priv * up,struct ucan_message_in * m)5659f2d3eaeSJakob Unterwurzacher static void ucan_rx_can_msg(struct ucan_priv *up, struct ucan_message_in *m)
5669f2d3eaeSJakob Unterwurzacher {
5679f2d3eaeSJakob Unterwurzacher 	int len;
5689f2d3eaeSJakob Unterwurzacher 	canid_t canid;
5699f2d3eaeSJakob Unterwurzacher 	struct can_frame *cf;
5709f2d3eaeSJakob Unterwurzacher 	struct sk_buff *skb;
5719f2d3eaeSJakob Unterwurzacher 	struct net_device_stats *stats = &up->netdev->stats;
5729f2d3eaeSJakob Unterwurzacher 
5739f2d3eaeSJakob Unterwurzacher 	/* get the contents of the length field */
5749f2d3eaeSJakob Unterwurzacher 	len = le16_to_cpu(m->len);
5759f2d3eaeSJakob Unterwurzacher 
5769f2d3eaeSJakob Unterwurzacher 	/* check sanity */
5779f2d3eaeSJakob Unterwurzacher 	if (len < UCAN_IN_HDR_SIZE + sizeof(m->msg.can_msg.id)) {
5789f2d3eaeSJakob Unterwurzacher 		netdev_warn(up->netdev, "invalid input message len: %d\n", len);
5799f2d3eaeSJakob Unterwurzacher 		return;
5809f2d3eaeSJakob Unterwurzacher 	}
5819f2d3eaeSJakob Unterwurzacher 
5829f2d3eaeSJakob Unterwurzacher 	/* handle error frames */
5839f2d3eaeSJakob Unterwurzacher 	canid = le32_to_cpu(m->msg.can_msg.id);
5849f2d3eaeSJakob Unterwurzacher 	if (canid & CAN_ERR_FLAG) {
5859f2d3eaeSJakob Unterwurzacher 		bool busstate_changed = ucan_handle_error_frame(up, m, canid);
5869f2d3eaeSJakob Unterwurzacher 
5879f2d3eaeSJakob Unterwurzacher 		/* if berr-reporting is off only state changes get through */
5889f2d3eaeSJakob Unterwurzacher 		if (!(up->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) &&
5899f2d3eaeSJakob Unterwurzacher 		    !busstate_changed)
5909f2d3eaeSJakob Unterwurzacher 			return;
5919f2d3eaeSJakob Unterwurzacher 	} else {
5929f2d3eaeSJakob Unterwurzacher 		canid_t canid_mask;
5939f2d3eaeSJakob Unterwurzacher 		/* compute the mask for canid */
5949f2d3eaeSJakob Unterwurzacher 		canid_mask = CAN_RTR_FLAG;
5959f2d3eaeSJakob Unterwurzacher 		if (canid & CAN_EFF_FLAG)
5969f2d3eaeSJakob Unterwurzacher 			canid_mask |= CAN_EFF_MASK | CAN_EFF_FLAG;
5979f2d3eaeSJakob Unterwurzacher 		else
5989f2d3eaeSJakob Unterwurzacher 			canid_mask |= CAN_SFF_MASK;
5999f2d3eaeSJakob Unterwurzacher 
6009f2d3eaeSJakob Unterwurzacher 		if (canid & ~canid_mask)
6019f2d3eaeSJakob Unterwurzacher 			netdev_warn(up->netdev,
6029f2d3eaeSJakob Unterwurzacher 				    "unexpected bits set (canid %x, mask %x)",
6039f2d3eaeSJakob Unterwurzacher 				    canid, canid_mask);
6049f2d3eaeSJakob Unterwurzacher 
6059f2d3eaeSJakob Unterwurzacher 		canid &= canid_mask;
6069f2d3eaeSJakob Unterwurzacher 	}
6079f2d3eaeSJakob Unterwurzacher 
6089f2d3eaeSJakob Unterwurzacher 	/* allocate skb */
6099f2d3eaeSJakob Unterwurzacher 	skb = alloc_can_skb(up->netdev, &cf);
6109f2d3eaeSJakob Unterwurzacher 	if (!skb)
6119f2d3eaeSJakob Unterwurzacher 		return;
6129f2d3eaeSJakob Unterwurzacher 
6139f2d3eaeSJakob Unterwurzacher 	/* fill the can frame */
6149f2d3eaeSJakob Unterwurzacher 	cf->can_id = canid;
6159f2d3eaeSJakob Unterwurzacher 
6169f2d3eaeSJakob Unterwurzacher 	/* compute DLC taking RTR_FLAG into account */
617c7b74967SOliver Hartkopp 	cf->len = ucan_can_cc_dlc2len(&m->msg.can_msg, len);
6189f2d3eaeSJakob Unterwurzacher 
6199f2d3eaeSJakob Unterwurzacher 	/* copy the payload of non RTR frames */
6209f2d3eaeSJakob Unterwurzacher 	if (!(cf->can_id & CAN_RTR_FLAG) || (cf->can_id & CAN_ERR_FLAG))
621c7b74967SOliver Hartkopp 		memcpy(cf->data, m->msg.can_msg.data, cf->len);
6229f2d3eaeSJakob Unterwurzacher 
6239f2d3eaeSJakob Unterwurzacher 	/* don't count error frames as real packets */
624676068dbSVincent Mailhol 	if (!(cf->can_id & CAN_ERR_FLAG)) {
6259f2d3eaeSJakob Unterwurzacher 		stats->rx_packets++;
6268e674ca7SVincent Mailhol 		if (!(cf->can_id & CAN_RTR_FLAG))
627c7b74967SOliver Hartkopp 			stats->rx_bytes += cf->len;
628676068dbSVincent Mailhol 	}
6299f2d3eaeSJakob Unterwurzacher 
6309f2d3eaeSJakob Unterwurzacher 	/* pass it to Linux */
6319f2d3eaeSJakob Unterwurzacher 	netif_rx(skb);
6329f2d3eaeSJakob Unterwurzacher }
6339f2d3eaeSJakob Unterwurzacher 
6349f2d3eaeSJakob Unterwurzacher /* callback indicating completed transmission */
ucan_tx_complete_msg(struct ucan_priv * up,struct ucan_message_in * m)6359f2d3eaeSJakob Unterwurzacher static void ucan_tx_complete_msg(struct ucan_priv *up,
6369f2d3eaeSJakob Unterwurzacher 				 struct ucan_message_in *m)
6379f2d3eaeSJakob Unterwurzacher {
6389f2d3eaeSJakob Unterwurzacher 	unsigned long flags;
6399f2d3eaeSJakob Unterwurzacher 	u16 count, i;
640cc4b08c3SVincent Mailhol 	u8 echo_index;
6419f2d3eaeSJakob Unterwurzacher 	u16 len = le16_to_cpu(m->len);
6429f2d3eaeSJakob Unterwurzacher 
6439f2d3eaeSJakob Unterwurzacher 	struct ucan_urb_context *context;
6449f2d3eaeSJakob Unterwurzacher 
6459f2d3eaeSJakob Unterwurzacher 	if (len < UCAN_IN_HDR_SIZE || (len % 2 != 0)) {
6469f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev, "invalid tx complete length\n");
6479f2d3eaeSJakob Unterwurzacher 		return;
6489f2d3eaeSJakob Unterwurzacher 	}
6499f2d3eaeSJakob Unterwurzacher 
6509f2d3eaeSJakob Unterwurzacher 	count = (len - UCAN_IN_HDR_SIZE) / 2;
6519f2d3eaeSJakob Unterwurzacher 	for (i = 0; i < count; i++) {
6529f2d3eaeSJakob Unterwurzacher 		/* we did not submit such echo ids */
6539f2d3eaeSJakob Unterwurzacher 		echo_index = m->msg.can_tx_complete_msg[i].echo_index;
6549f2d3eaeSJakob Unterwurzacher 		if (echo_index >= up->device_info.tx_fifo) {
6559f2d3eaeSJakob Unterwurzacher 			up->netdev->stats.tx_errors++;
6569f2d3eaeSJakob Unterwurzacher 			netdev_err(up->netdev,
6579f2d3eaeSJakob Unterwurzacher 				   "invalid echo_index %d received\n",
6589f2d3eaeSJakob Unterwurzacher 				   echo_index);
6599f2d3eaeSJakob Unterwurzacher 			continue;
6609f2d3eaeSJakob Unterwurzacher 		}
6619f2d3eaeSJakob Unterwurzacher 
6629f2d3eaeSJakob Unterwurzacher 		/* gather information from the context */
6639f2d3eaeSJakob Unterwurzacher 		context = &up->context_array[echo_index];
6649f2d3eaeSJakob Unterwurzacher 
6659f2d3eaeSJakob Unterwurzacher 		/* Release context and restart queue if necessary.
6669f2d3eaeSJakob Unterwurzacher 		 * Also check if the context was allocated
6679f2d3eaeSJakob Unterwurzacher 		 */
6689f2d3eaeSJakob Unterwurzacher 		if (!ucan_release_context(up, context))
6699f2d3eaeSJakob Unterwurzacher 			continue;
6709f2d3eaeSJakob Unterwurzacher 
6719f2d3eaeSJakob Unterwurzacher 		spin_lock_irqsave(&up->echo_skb_lock, flags);
6729f2d3eaeSJakob Unterwurzacher 		if (m->msg.can_tx_complete_msg[i].flags &
6739f2d3eaeSJakob Unterwurzacher 		    UCAN_TX_COMPLETE_SUCCESS) {
6749f2d3eaeSJakob Unterwurzacher 			/* update statistics */
6759f2d3eaeSJakob Unterwurzacher 			up->netdev->stats.tx_packets++;
676cc4b08c3SVincent Mailhol 			up->netdev->stats.tx_bytes +=
6779420e1d4SMarc Kleine-Budde 				can_get_echo_skb(up->netdev, echo_index, NULL);
6789f2d3eaeSJakob Unterwurzacher 		} else {
6799f2d3eaeSJakob Unterwurzacher 			up->netdev->stats.tx_dropped++;
680f318482aSMarc Kleine-Budde 			can_free_echo_skb(up->netdev, echo_index, NULL);
6819f2d3eaeSJakob Unterwurzacher 		}
6829f2d3eaeSJakob Unterwurzacher 		spin_unlock_irqrestore(&up->echo_skb_lock, flags);
6839f2d3eaeSJakob Unterwurzacher 	}
6849f2d3eaeSJakob Unterwurzacher }
6859f2d3eaeSJakob Unterwurzacher 
6869f2d3eaeSJakob Unterwurzacher /* callback on reception of a USB message */
ucan_read_bulk_callback(struct urb * urb)6879f2d3eaeSJakob Unterwurzacher static void ucan_read_bulk_callback(struct urb *urb)
6889f2d3eaeSJakob Unterwurzacher {
6899f2d3eaeSJakob Unterwurzacher 	int ret;
6909f2d3eaeSJakob Unterwurzacher 	int pos;
6919f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up = urb->context;
6929f2d3eaeSJakob Unterwurzacher 	struct net_device *netdev = up->netdev;
6939f2d3eaeSJakob Unterwurzacher 	struct ucan_message_in *m;
6949f2d3eaeSJakob Unterwurzacher 
6959f2d3eaeSJakob Unterwurzacher 	/* the device is not up and the driver should not receive any
6969f2d3eaeSJakob Unterwurzacher 	 * data on the bulk in pipe
6979f2d3eaeSJakob Unterwurzacher 	 */
6989f2d3eaeSJakob Unterwurzacher 	if (WARN_ON(!up->context_array)) {
6999f2d3eaeSJakob Unterwurzacher 		usb_free_coherent(up->udev,
7009f2d3eaeSJakob Unterwurzacher 				  up->in_ep_size,
7019f2d3eaeSJakob Unterwurzacher 				  urb->transfer_buffer,
7029f2d3eaeSJakob Unterwurzacher 				  urb->transfer_dma);
7039f2d3eaeSJakob Unterwurzacher 		return;
7049f2d3eaeSJakob Unterwurzacher 	}
7059f2d3eaeSJakob Unterwurzacher 
7069f2d3eaeSJakob Unterwurzacher 	/* check URB status */
7079f2d3eaeSJakob Unterwurzacher 	switch (urb->status) {
7089f2d3eaeSJakob Unterwurzacher 	case 0:
7099f2d3eaeSJakob Unterwurzacher 		break;
7109f2d3eaeSJakob Unterwurzacher 	case -ENOENT:
7119f2d3eaeSJakob Unterwurzacher 	case -EPIPE:
7129f2d3eaeSJakob Unterwurzacher 	case -EPROTO:
7139f2d3eaeSJakob Unterwurzacher 	case -ESHUTDOWN:
7149f2d3eaeSJakob Unterwurzacher 	case -ETIME:
7159f2d3eaeSJakob Unterwurzacher 		/* urb is not resubmitted -> free dma data */
7169f2d3eaeSJakob Unterwurzacher 		usb_free_coherent(up->udev,
7179f2d3eaeSJakob Unterwurzacher 				  up->in_ep_size,
7189f2d3eaeSJakob Unterwurzacher 				  urb->transfer_buffer,
7199f2d3eaeSJakob Unterwurzacher 				  urb->transfer_dma);
7203b17d417SColin Ian King 		netdev_dbg(up->netdev, "not resubmitting urb; status: %d\n",
7219f2d3eaeSJakob Unterwurzacher 			   urb->status);
7229f2d3eaeSJakob Unterwurzacher 		return;
7239f2d3eaeSJakob Unterwurzacher 	default:
7249f2d3eaeSJakob Unterwurzacher 		goto resubmit;
7259f2d3eaeSJakob Unterwurzacher 	}
7269f2d3eaeSJakob Unterwurzacher 
7279f2d3eaeSJakob Unterwurzacher 	/* sanity check */
7289f2d3eaeSJakob Unterwurzacher 	if (!netif_device_present(netdev))
7299f2d3eaeSJakob Unterwurzacher 		return;
7309f2d3eaeSJakob Unterwurzacher 
7319f2d3eaeSJakob Unterwurzacher 	/* iterate over input */
7329f2d3eaeSJakob Unterwurzacher 	pos = 0;
7339f2d3eaeSJakob Unterwurzacher 	while (pos < urb->actual_length) {
7349f2d3eaeSJakob Unterwurzacher 		int len;
7359f2d3eaeSJakob Unterwurzacher 
7369f2d3eaeSJakob Unterwurzacher 		/* check sanity (length of header) */
7379f2d3eaeSJakob Unterwurzacher 		if ((urb->actual_length - pos) < UCAN_IN_HDR_SIZE) {
7389f2d3eaeSJakob Unterwurzacher 			netdev_warn(up->netdev,
7399f2d3eaeSJakob Unterwurzacher 				    "invalid message (short; no hdr; l:%d)\n",
7409f2d3eaeSJakob Unterwurzacher 				    urb->actual_length);
7419f2d3eaeSJakob Unterwurzacher 			goto resubmit;
7429f2d3eaeSJakob Unterwurzacher 		}
7439f2d3eaeSJakob Unterwurzacher 
7449f2d3eaeSJakob Unterwurzacher 		/* setup the message address */
7459f2d3eaeSJakob Unterwurzacher 		m = (struct ucan_message_in *)
7469f2d3eaeSJakob Unterwurzacher 			((u8 *)urb->transfer_buffer + pos);
7479f2d3eaeSJakob Unterwurzacher 		len = le16_to_cpu(m->len);
7489f2d3eaeSJakob Unterwurzacher 
7499f2d3eaeSJakob Unterwurzacher 		/* check sanity (length of content) */
7509f2d3eaeSJakob Unterwurzacher 		if (urb->actual_length - pos < len) {
7519f2d3eaeSJakob Unterwurzacher 			netdev_warn(up->netdev,
7529f2d3eaeSJakob Unterwurzacher 				    "invalid message (short; no data; l:%d)\n",
7539f2d3eaeSJakob Unterwurzacher 				    urb->actual_length);
7549f2d3eaeSJakob Unterwurzacher 			print_hex_dump(KERN_WARNING,
7559f2d3eaeSJakob Unterwurzacher 				       "raw data: ",
7569f2d3eaeSJakob Unterwurzacher 				       DUMP_PREFIX_ADDRESS,
7579f2d3eaeSJakob Unterwurzacher 				       16,
7589f2d3eaeSJakob Unterwurzacher 				       1,
7599f2d3eaeSJakob Unterwurzacher 				       urb->transfer_buffer,
7609f2d3eaeSJakob Unterwurzacher 				       urb->actual_length,
7619f2d3eaeSJakob Unterwurzacher 				       true);
7629f2d3eaeSJakob Unterwurzacher 
7639f2d3eaeSJakob Unterwurzacher 			goto resubmit;
7649f2d3eaeSJakob Unterwurzacher 		}
7659f2d3eaeSJakob Unterwurzacher 
7669f2d3eaeSJakob Unterwurzacher 		switch (m->type) {
7679f2d3eaeSJakob Unterwurzacher 		case UCAN_IN_RX:
7689f2d3eaeSJakob Unterwurzacher 			ucan_rx_can_msg(up, m);
7699f2d3eaeSJakob Unterwurzacher 			break;
7709f2d3eaeSJakob Unterwurzacher 		case UCAN_IN_TX_COMPLETE:
7719f2d3eaeSJakob Unterwurzacher 			ucan_tx_complete_msg(up, m);
7729f2d3eaeSJakob Unterwurzacher 			break;
7739f2d3eaeSJakob Unterwurzacher 		default:
7749f2d3eaeSJakob Unterwurzacher 			netdev_warn(up->netdev,
7759f2d3eaeSJakob Unterwurzacher 				    "invalid message (type; t:%d)\n",
7769f2d3eaeSJakob Unterwurzacher 				    m->type);
7779f2d3eaeSJakob Unterwurzacher 			break;
7789f2d3eaeSJakob Unterwurzacher 		}
7799f2d3eaeSJakob Unterwurzacher 
7809f2d3eaeSJakob Unterwurzacher 		/* proceed to next message */
7819f2d3eaeSJakob Unterwurzacher 		pos += len;
7829f2d3eaeSJakob Unterwurzacher 		/* align to 4 byte boundary */
7839f2d3eaeSJakob Unterwurzacher 		pos = round_up(pos, 4);
7849f2d3eaeSJakob Unterwurzacher 	}
7859f2d3eaeSJakob Unterwurzacher 
7869f2d3eaeSJakob Unterwurzacher resubmit:
7879f2d3eaeSJakob Unterwurzacher 	/* resubmit urb when done */
7889f2d3eaeSJakob Unterwurzacher 	usb_fill_bulk_urb(urb, up->udev,
7899f2d3eaeSJakob Unterwurzacher 			  usb_rcvbulkpipe(up->udev,
7909f2d3eaeSJakob Unterwurzacher 					  up->in_ep_addr),
7919f2d3eaeSJakob Unterwurzacher 			  urb->transfer_buffer,
7929f2d3eaeSJakob Unterwurzacher 			  up->in_ep_size,
7939f2d3eaeSJakob Unterwurzacher 			  ucan_read_bulk_callback,
7949f2d3eaeSJakob Unterwurzacher 			  up);
7959f2d3eaeSJakob Unterwurzacher 
7969f2d3eaeSJakob Unterwurzacher 	usb_anchor_urb(urb, &up->rx_urbs);
797870db5d1SJohan Hovold 	ret = usb_submit_urb(urb, GFP_ATOMIC);
7989f2d3eaeSJakob Unterwurzacher 
7999f2d3eaeSJakob Unterwurzacher 	if (ret < 0) {
8009f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev,
8019f2d3eaeSJakob Unterwurzacher 			   "failed resubmitting read bulk urb: %d\n",
8029f2d3eaeSJakob Unterwurzacher 			   ret);
8039f2d3eaeSJakob Unterwurzacher 
8049f2d3eaeSJakob Unterwurzacher 		usb_unanchor_urb(urb);
8059f2d3eaeSJakob Unterwurzacher 		usb_free_coherent(up->udev,
8069f2d3eaeSJakob Unterwurzacher 				  up->in_ep_size,
8079f2d3eaeSJakob Unterwurzacher 				  urb->transfer_buffer,
8089f2d3eaeSJakob Unterwurzacher 				  urb->transfer_dma);
8099f2d3eaeSJakob Unterwurzacher 
8109f2d3eaeSJakob Unterwurzacher 		if (ret == -ENODEV)
8119f2d3eaeSJakob Unterwurzacher 			netif_device_detach(netdev);
8129f2d3eaeSJakob Unterwurzacher 	}
8139f2d3eaeSJakob Unterwurzacher }
8149f2d3eaeSJakob Unterwurzacher 
8159f2d3eaeSJakob Unterwurzacher /* callback after transmission of a USB message */
ucan_write_bulk_callback(struct urb * urb)8169f2d3eaeSJakob Unterwurzacher static void ucan_write_bulk_callback(struct urb *urb)
8179f2d3eaeSJakob Unterwurzacher {
8189f2d3eaeSJakob Unterwurzacher 	unsigned long flags;
8199f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up;
8209f2d3eaeSJakob Unterwurzacher 	struct ucan_urb_context *context = urb->context;
8219f2d3eaeSJakob Unterwurzacher 
8229f2d3eaeSJakob Unterwurzacher 	/* get the urb context */
8239f2d3eaeSJakob Unterwurzacher 	if (WARN_ON_ONCE(!context))
8249f2d3eaeSJakob Unterwurzacher 		return;
8259f2d3eaeSJakob Unterwurzacher 
8269f2d3eaeSJakob Unterwurzacher 	/* free up our allocated buffer */
8279f2d3eaeSJakob Unterwurzacher 	usb_free_coherent(urb->dev,
8289f2d3eaeSJakob Unterwurzacher 			  sizeof(struct ucan_message_out),
8299f2d3eaeSJakob Unterwurzacher 			  urb->transfer_buffer,
8309f2d3eaeSJakob Unterwurzacher 			  urb->transfer_dma);
8319f2d3eaeSJakob Unterwurzacher 
8329f2d3eaeSJakob Unterwurzacher 	up = context->up;
8339f2d3eaeSJakob Unterwurzacher 	if (WARN_ON_ONCE(!up))
8349f2d3eaeSJakob Unterwurzacher 		return;
8359f2d3eaeSJakob Unterwurzacher 
8369f2d3eaeSJakob Unterwurzacher 	/* sanity check */
8379f2d3eaeSJakob Unterwurzacher 	if (!netif_device_present(up->netdev))
8389f2d3eaeSJakob Unterwurzacher 		return;
8399f2d3eaeSJakob Unterwurzacher 
8409f2d3eaeSJakob Unterwurzacher 	/* transmission failed (USB - the device will not send a TX complete) */
8419f2d3eaeSJakob Unterwurzacher 	if (urb->status) {
8429f2d3eaeSJakob Unterwurzacher 		netdev_warn(up->netdev,
8439f2d3eaeSJakob Unterwurzacher 			    "failed to transmit USB message to device: %d\n",
8449f2d3eaeSJakob Unterwurzacher 			     urb->status);
8459f2d3eaeSJakob Unterwurzacher 
8469f2d3eaeSJakob Unterwurzacher 		/* update counters an cleanup */
8479f2d3eaeSJakob Unterwurzacher 		spin_lock_irqsave(&up->echo_skb_lock, flags);
848f318482aSMarc Kleine-Budde 		can_free_echo_skb(up->netdev, context - up->context_array, NULL);
8499f2d3eaeSJakob Unterwurzacher 		spin_unlock_irqrestore(&up->echo_skb_lock, flags);
8509f2d3eaeSJakob Unterwurzacher 
8519f2d3eaeSJakob Unterwurzacher 		up->netdev->stats.tx_dropped++;
8529f2d3eaeSJakob Unterwurzacher 
8539f2d3eaeSJakob Unterwurzacher 		/* release context and restart the queue if necessary */
8549f2d3eaeSJakob Unterwurzacher 		if (!ucan_release_context(up, context))
8559f2d3eaeSJakob Unterwurzacher 			netdev_err(up->netdev,
8569f2d3eaeSJakob Unterwurzacher 				   "urb failed, failed to release context\n");
8579f2d3eaeSJakob Unterwurzacher 	}
8589f2d3eaeSJakob Unterwurzacher }
8599f2d3eaeSJakob Unterwurzacher 
ucan_cleanup_rx_urbs(struct ucan_priv * up,struct urb ** urbs)8609f2d3eaeSJakob Unterwurzacher static void ucan_cleanup_rx_urbs(struct ucan_priv *up, struct urb **urbs)
8619f2d3eaeSJakob Unterwurzacher {
8629f2d3eaeSJakob Unterwurzacher 	int i;
8639f2d3eaeSJakob Unterwurzacher 
8649f2d3eaeSJakob Unterwurzacher 	for (i = 0; i < UCAN_MAX_RX_URBS; i++) {
8659f2d3eaeSJakob Unterwurzacher 		if (urbs[i]) {
8669f2d3eaeSJakob Unterwurzacher 			usb_unanchor_urb(urbs[i]);
8679f2d3eaeSJakob Unterwurzacher 			usb_free_coherent(up->udev,
8689f2d3eaeSJakob Unterwurzacher 					  up->in_ep_size,
8699f2d3eaeSJakob Unterwurzacher 					  urbs[i]->transfer_buffer,
8709f2d3eaeSJakob Unterwurzacher 					  urbs[i]->transfer_dma);
8719f2d3eaeSJakob Unterwurzacher 			usb_free_urb(urbs[i]);
8729f2d3eaeSJakob Unterwurzacher 		}
8739f2d3eaeSJakob Unterwurzacher 	}
8749f2d3eaeSJakob Unterwurzacher 
8759f2d3eaeSJakob Unterwurzacher 	memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS);
8769f2d3eaeSJakob Unterwurzacher }
8779f2d3eaeSJakob Unterwurzacher 
ucan_prepare_and_anchor_rx_urbs(struct ucan_priv * up,struct urb ** urbs)8789f2d3eaeSJakob Unterwurzacher static int ucan_prepare_and_anchor_rx_urbs(struct ucan_priv *up,
8799f2d3eaeSJakob Unterwurzacher 					   struct urb **urbs)
8809f2d3eaeSJakob Unterwurzacher {
8819f2d3eaeSJakob Unterwurzacher 	int i;
8829f2d3eaeSJakob Unterwurzacher 
8839f2d3eaeSJakob Unterwurzacher 	memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS);
8849f2d3eaeSJakob Unterwurzacher 
8859f2d3eaeSJakob Unterwurzacher 	for (i = 0; i < UCAN_MAX_RX_URBS; i++) {
8869f2d3eaeSJakob Unterwurzacher 		void *buf;
8879f2d3eaeSJakob Unterwurzacher 
8889f2d3eaeSJakob Unterwurzacher 		urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
8899f2d3eaeSJakob Unterwurzacher 		if (!urbs[i])
8909f2d3eaeSJakob Unterwurzacher 			goto err;
8919f2d3eaeSJakob Unterwurzacher 
8929f2d3eaeSJakob Unterwurzacher 		buf = usb_alloc_coherent(up->udev,
8939f2d3eaeSJakob Unterwurzacher 					 up->in_ep_size,
8949f2d3eaeSJakob Unterwurzacher 					 GFP_KERNEL, &urbs[i]->transfer_dma);
8959f2d3eaeSJakob Unterwurzacher 		if (!buf) {
8969f2d3eaeSJakob Unterwurzacher 			/* cleanup this urb */
8979f2d3eaeSJakob Unterwurzacher 			usb_free_urb(urbs[i]);
8989f2d3eaeSJakob Unterwurzacher 			urbs[i] = NULL;
8999f2d3eaeSJakob Unterwurzacher 			goto err;
9009f2d3eaeSJakob Unterwurzacher 		}
9019f2d3eaeSJakob Unterwurzacher 
9029f2d3eaeSJakob Unterwurzacher 		usb_fill_bulk_urb(urbs[i], up->udev,
9039f2d3eaeSJakob Unterwurzacher 				  usb_rcvbulkpipe(up->udev,
9049f2d3eaeSJakob Unterwurzacher 						  up->in_ep_addr),
9059f2d3eaeSJakob Unterwurzacher 				  buf,
9069f2d3eaeSJakob Unterwurzacher 				  up->in_ep_size,
9079f2d3eaeSJakob Unterwurzacher 				  ucan_read_bulk_callback,
9089f2d3eaeSJakob Unterwurzacher 				  up);
9099f2d3eaeSJakob Unterwurzacher 
9109f2d3eaeSJakob Unterwurzacher 		urbs[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
9119f2d3eaeSJakob Unterwurzacher 
9129f2d3eaeSJakob Unterwurzacher 		usb_anchor_urb(urbs[i], &up->rx_urbs);
9139f2d3eaeSJakob Unterwurzacher 	}
9149f2d3eaeSJakob Unterwurzacher 	return 0;
9159f2d3eaeSJakob Unterwurzacher 
9169f2d3eaeSJakob Unterwurzacher err:
9179f2d3eaeSJakob Unterwurzacher 	/* cleanup other unsubmitted urbs */
9189f2d3eaeSJakob Unterwurzacher 	ucan_cleanup_rx_urbs(up, urbs);
9199f2d3eaeSJakob Unterwurzacher 	return -ENOMEM;
9209f2d3eaeSJakob Unterwurzacher }
9219f2d3eaeSJakob Unterwurzacher 
9229f2d3eaeSJakob Unterwurzacher /* Submits rx urbs with the semantic: Either submit all, or cleanup
9239f2d3eaeSJakob Unterwurzacher  * everything. I case of errors submitted urbs are killed and all urbs in
9249f2d3eaeSJakob Unterwurzacher  * the array are freed. I case of no errors every entry in the urb
9259f2d3eaeSJakob Unterwurzacher  * array is set to NULL.
9269f2d3eaeSJakob Unterwurzacher  */
ucan_submit_rx_urbs(struct ucan_priv * up,struct urb ** urbs)9279f2d3eaeSJakob Unterwurzacher static int ucan_submit_rx_urbs(struct ucan_priv *up, struct urb **urbs)
9289f2d3eaeSJakob Unterwurzacher {
9299f2d3eaeSJakob Unterwurzacher 	int i, ret;
9309f2d3eaeSJakob Unterwurzacher 
9319f2d3eaeSJakob Unterwurzacher 	/* Iterate over all urbs to submit. On success remove the urb
9329f2d3eaeSJakob Unterwurzacher 	 * from the list.
9339f2d3eaeSJakob Unterwurzacher 	 */
9349f2d3eaeSJakob Unterwurzacher 	for (i = 0; i < UCAN_MAX_RX_URBS; i++) {
9359f2d3eaeSJakob Unterwurzacher 		ret = usb_submit_urb(urbs[i], GFP_KERNEL);
9369f2d3eaeSJakob Unterwurzacher 		if (ret) {
9379f2d3eaeSJakob Unterwurzacher 			netdev_err(up->netdev,
9389f2d3eaeSJakob Unterwurzacher 				   "could not submit urb; code: %d\n",
9399f2d3eaeSJakob Unterwurzacher 				   ret);
9409f2d3eaeSJakob Unterwurzacher 			goto err;
9419f2d3eaeSJakob Unterwurzacher 		}
9429f2d3eaeSJakob Unterwurzacher 
9439f2d3eaeSJakob Unterwurzacher 		/* Anchor URB and drop reference, USB core will take
9449f2d3eaeSJakob Unterwurzacher 		 * care of freeing it
9459f2d3eaeSJakob Unterwurzacher 		 */
9469f2d3eaeSJakob Unterwurzacher 		usb_free_urb(urbs[i]);
9479f2d3eaeSJakob Unterwurzacher 		urbs[i] = NULL;
9489f2d3eaeSJakob Unterwurzacher 	}
9499f2d3eaeSJakob Unterwurzacher 	return 0;
9509f2d3eaeSJakob Unterwurzacher 
9519f2d3eaeSJakob Unterwurzacher err:
9529f2d3eaeSJakob Unterwurzacher 	/* Cleanup unsubmitted urbs */
9539f2d3eaeSJakob Unterwurzacher 	ucan_cleanup_rx_urbs(up, urbs);
9549f2d3eaeSJakob Unterwurzacher 
9559f2d3eaeSJakob Unterwurzacher 	/* Kill urbs that are already submitted */
9569f2d3eaeSJakob Unterwurzacher 	usb_kill_anchored_urbs(&up->rx_urbs);
9579f2d3eaeSJakob Unterwurzacher 
9589f2d3eaeSJakob Unterwurzacher 	return ret;
9599f2d3eaeSJakob Unterwurzacher }
9609f2d3eaeSJakob Unterwurzacher 
9619f2d3eaeSJakob Unterwurzacher /* Open the network device */
ucan_open(struct net_device * netdev)9629f2d3eaeSJakob Unterwurzacher static int ucan_open(struct net_device *netdev)
9639f2d3eaeSJakob Unterwurzacher {
9649f2d3eaeSJakob Unterwurzacher 	int ret, ret_cleanup;
9659f2d3eaeSJakob Unterwurzacher 	u16 ctrlmode;
9669f2d3eaeSJakob Unterwurzacher 	struct urb *urbs[UCAN_MAX_RX_URBS];
9679f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up = netdev_priv(netdev);
9689f2d3eaeSJakob Unterwurzacher 
9699f2d3eaeSJakob Unterwurzacher 	ret = ucan_alloc_context_array(up);
9709f2d3eaeSJakob Unterwurzacher 	if (ret)
9719f2d3eaeSJakob Unterwurzacher 		return ret;
9729f2d3eaeSJakob Unterwurzacher 
9739f2d3eaeSJakob Unterwurzacher 	/* Allocate and prepare IN URBS - allocated and anchored
9749f2d3eaeSJakob Unterwurzacher 	 * urbs are stored in urbs[] for clean
9759f2d3eaeSJakob Unterwurzacher 	 */
9769f2d3eaeSJakob Unterwurzacher 	ret = ucan_prepare_and_anchor_rx_urbs(up, urbs);
9779f2d3eaeSJakob Unterwurzacher 	if (ret)
9789f2d3eaeSJakob Unterwurzacher 		goto err_contexts;
9799f2d3eaeSJakob Unterwurzacher 
9809f2d3eaeSJakob Unterwurzacher 	/* Check the control mode */
9819f2d3eaeSJakob Unterwurzacher 	ctrlmode = 0;
9829f2d3eaeSJakob Unterwurzacher 	if (up->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
9839f2d3eaeSJakob Unterwurzacher 		ctrlmode |= UCAN_MODE_LOOPBACK;
9849f2d3eaeSJakob Unterwurzacher 	if (up->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
9859f2d3eaeSJakob Unterwurzacher 		ctrlmode |= UCAN_MODE_SILENT;
9869f2d3eaeSJakob Unterwurzacher 	if (up->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
9879f2d3eaeSJakob Unterwurzacher 		ctrlmode |= UCAN_MODE_3_SAMPLES;
9889f2d3eaeSJakob Unterwurzacher 	if (up->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
9899f2d3eaeSJakob Unterwurzacher 		ctrlmode |= UCAN_MODE_ONE_SHOT;
9909f2d3eaeSJakob Unterwurzacher 
9919f2d3eaeSJakob Unterwurzacher 	/* Enable this in any case - filtering is down within the
9929f2d3eaeSJakob Unterwurzacher 	 * receive path
9939f2d3eaeSJakob Unterwurzacher 	 */
9949f2d3eaeSJakob Unterwurzacher 	ctrlmode |= UCAN_MODE_BERR_REPORT;
9959f2d3eaeSJakob Unterwurzacher 	up->ctl_msg_buffer->cmd_start.mode = cpu_to_le16(ctrlmode);
9969f2d3eaeSJakob Unterwurzacher 
9979f2d3eaeSJakob Unterwurzacher 	/* Driver is ready to receive data - start the USB device */
9989f2d3eaeSJakob Unterwurzacher 	ret = ucan_ctrl_command_out(up, UCAN_COMMAND_START, 0, 2);
9999f2d3eaeSJakob Unterwurzacher 	if (ret < 0) {
10009f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev,
10019f2d3eaeSJakob Unterwurzacher 			   "could not start device, code: %d\n",
10029f2d3eaeSJakob Unterwurzacher 			   ret);
10039f2d3eaeSJakob Unterwurzacher 		goto err_reset;
10049f2d3eaeSJakob Unterwurzacher 	}
10059f2d3eaeSJakob Unterwurzacher 
10069f2d3eaeSJakob Unterwurzacher 	/* Call CAN layer open */
10079f2d3eaeSJakob Unterwurzacher 	ret = open_candev(netdev);
10089f2d3eaeSJakob Unterwurzacher 	if (ret)
10099f2d3eaeSJakob Unterwurzacher 		goto err_stop;
10109f2d3eaeSJakob Unterwurzacher 
10119f2d3eaeSJakob Unterwurzacher 	/* Driver is ready to receive data. Submit RX URBS */
10129f2d3eaeSJakob Unterwurzacher 	ret = ucan_submit_rx_urbs(up, urbs);
10139f2d3eaeSJakob Unterwurzacher 	if (ret)
10149f2d3eaeSJakob Unterwurzacher 		goto err_stop;
10159f2d3eaeSJakob Unterwurzacher 
10169f2d3eaeSJakob Unterwurzacher 	up->can.state = CAN_STATE_ERROR_ACTIVE;
10179f2d3eaeSJakob Unterwurzacher 
10189f2d3eaeSJakob Unterwurzacher 	/* Start the network queue */
10199f2d3eaeSJakob Unterwurzacher 	netif_start_queue(netdev);
10209f2d3eaeSJakob Unterwurzacher 
10219f2d3eaeSJakob Unterwurzacher 	return 0;
10229f2d3eaeSJakob Unterwurzacher 
10239f2d3eaeSJakob Unterwurzacher err_stop:
10249f2d3eaeSJakob Unterwurzacher 	/* The device have started already stop it */
10259f2d3eaeSJakob Unterwurzacher 	ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_STOP, 0, 0);
10269f2d3eaeSJakob Unterwurzacher 	if (ret_cleanup < 0)
10279f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev,
10289f2d3eaeSJakob Unterwurzacher 			   "could not stop device, code: %d\n",
10299f2d3eaeSJakob Unterwurzacher 			   ret_cleanup);
10309f2d3eaeSJakob Unterwurzacher 
10319f2d3eaeSJakob Unterwurzacher err_reset:
10329f2d3eaeSJakob Unterwurzacher 	/* The device might have received data, reset it for
10339f2d3eaeSJakob Unterwurzacher 	 * consistent state
10349f2d3eaeSJakob Unterwurzacher 	 */
10359f2d3eaeSJakob Unterwurzacher 	ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0);
10369f2d3eaeSJakob Unterwurzacher 	if (ret_cleanup < 0)
10379f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev,
10389f2d3eaeSJakob Unterwurzacher 			   "could not reset device, code: %d\n",
10399f2d3eaeSJakob Unterwurzacher 			   ret_cleanup);
10409f2d3eaeSJakob Unterwurzacher 
10419f2d3eaeSJakob Unterwurzacher 	/* clean up unsubmitted urbs */
10429f2d3eaeSJakob Unterwurzacher 	ucan_cleanup_rx_urbs(up, urbs);
10439f2d3eaeSJakob Unterwurzacher 
10449f2d3eaeSJakob Unterwurzacher err_contexts:
10459f2d3eaeSJakob Unterwurzacher 	ucan_release_context_array(up);
10469f2d3eaeSJakob Unterwurzacher 	return ret;
10479f2d3eaeSJakob Unterwurzacher }
10489f2d3eaeSJakob Unterwurzacher 
ucan_prepare_tx_urb(struct ucan_priv * up,struct ucan_urb_context * context,struct can_frame * cf,u8 echo_index)10499f2d3eaeSJakob Unterwurzacher static struct urb *ucan_prepare_tx_urb(struct ucan_priv *up,
10509f2d3eaeSJakob Unterwurzacher 				       struct ucan_urb_context *context,
10519f2d3eaeSJakob Unterwurzacher 				       struct can_frame *cf,
10529f2d3eaeSJakob Unterwurzacher 				       u8 echo_index)
10539f2d3eaeSJakob Unterwurzacher {
10549f2d3eaeSJakob Unterwurzacher 	int mlen;
10559f2d3eaeSJakob Unterwurzacher 	struct urb *urb;
10569f2d3eaeSJakob Unterwurzacher 	struct ucan_message_out *m;
10579f2d3eaeSJakob Unterwurzacher 
10589f2d3eaeSJakob Unterwurzacher 	/* create a URB, and a buffer for it, and copy the data to the URB */
10599f2d3eaeSJakob Unterwurzacher 	urb = usb_alloc_urb(0, GFP_ATOMIC);
10609f2d3eaeSJakob Unterwurzacher 	if (!urb) {
10619f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev, "no memory left for URBs\n");
10629f2d3eaeSJakob Unterwurzacher 		return NULL;
10639f2d3eaeSJakob Unterwurzacher 	}
10649f2d3eaeSJakob Unterwurzacher 
10659f2d3eaeSJakob Unterwurzacher 	m = usb_alloc_coherent(up->udev,
10669f2d3eaeSJakob Unterwurzacher 			       sizeof(struct ucan_message_out),
10679f2d3eaeSJakob Unterwurzacher 			       GFP_ATOMIC,
10689f2d3eaeSJakob Unterwurzacher 			       &urb->transfer_dma);
10699f2d3eaeSJakob Unterwurzacher 	if (!m) {
10709f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev, "no memory left for USB buffer\n");
10719f2d3eaeSJakob Unterwurzacher 		usb_free_urb(urb);
10729f2d3eaeSJakob Unterwurzacher 		return NULL;
10739f2d3eaeSJakob Unterwurzacher 	}
10749f2d3eaeSJakob Unterwurzacher 
10759f2d3eaeSJakob Unterwurzacher 	/* build the USB message */
10769f2d3eaeSJakob Unterwurzacher 	m->type = UCAN_OUT_TX;
10779f2d3eaeSJakob Unterwurzacher 	m->msg.can_msg.id = cpu_to_le32(cf->can_id);
10789f2d3eaeSJakob Unterwurzacher 
10799f2d3eaeSJakob Unterwurzacher 	if (cf->can_id & CAN_RTR_FLAG) {
10809f2d3eaeSJakob Unterwurzacher 		mlen = UCAN_OUT_HDR_SIZE +
10819f2d3eaeSJakob Unterwurzacher 			offsetof(struct ucan_can_msg, dlc) +
10829f2d3eaeSJakob Unterwurzacher 			sizeof(m->msg.can_msg.dlc);
1083c7b74967SOliver Hartkopp 		m->msg.can_msg.dlc = cf->len;
10849f2d3eaeSJakob Unterwurzacher 	} else {
10859f2d3eaeSJakob Unterwurzacher 		mlen = UCAN_OUT_HDR_SIZE +
1086c7b74967SOliver Hartkopp 			sizeof(m->msg.can_msg.id) + cf->len;
1087c7b74967SOliver Hartkopp 		memcpy(m->msg.can_msg.data, cf->data, cf->len);
10889f2d3eaeSJakob Unterwurzacher 	}
10899f2d3eaeSJakob Unterwurzacher 	m->len = cpu_to_le16(mlen);
10909f2d3eaeSJakob Unterwurzacher 
10919f2d3eaeSJakob Unterwurzacher 	m->subtype = echo_index;
10929f2d3eaeSJakob Unterwurzacher 
10939f2d3eaeSJakob Unterwurzacher 	/* build the urb */
10949f2d3eaeSJakob Unterwurzacher 	usb_fill_bulk_urb(urb, up->udev,
10959f2d3eaeSJakob Unterwurzacher 			  usb_sndbulkpipe(up->udev,
10969f2d3eaeSJakob Unterwurzacher 					  up->out_ep_addr),
10979f2d3eaeSJakob Unterwurzacher 			  m, mlen, ucan_write_bulk_callback, context);
10989f2d3eaeSJakob Unterwurzacher 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
10999f2d3eaeSJakob Unterwurzacher 
11009f2d3eaeSJakob Unterwurzacher 	return urb;
11019f2d3eaeSJakob Unterwurzacher }
11029f2d3eaeSJakob Unterwurzacher 
ucan_clean_up_tx_urb(struct ucan_priv * up,struct urb * urb)11039f2d3eaeSJakob Unterwurzacher static void ucan_clean_up_tx_urb(struct ucan_priv *up, struct urb *urb)
11049f2d3eaeSJakob Unterwurzacher {
11059f2d3eaeSJakob Unterwurzacher 	usb_free_coherent(up->udev, sizeof(struct ucan_message_out),
11069f2d3eaeSJakob Unterwurzacher 			  urb->transfer_buffer, urb->transfer_dma);
11079f2d3eaeSJakob Unterwurzacher 	usb_free_urb(urb);
11089f2d3eaeSJakob Unterwurzacher }
11099f2d3eaeSJakob Unterwurzacher 
11109f2d3eaeSJakob Unterwurzacher /* callback when Linux needs to send a can frame */
ucan_start_xmit(struct sk_buff * skb,struct net_device * netdev)11119f2d3eaeSJakob Unterwurzacher static netdev_tx_t ucan_start_xmit(struct sk_buff *skb,
11129f2d3eaeSJakob Unterwurzacher 				   struct net_device *netdev)
11139f2d3eaeSJakob Unterwurzacher {
11149f2d3eaeSJakob Unterwurzacher 	unsigned long flags;
11159f2d3eaeSJakob Unterwurzacher 	int ret;
11169f2d3eaeSJakob Unterwurzacher 	u8 echo_index;
11179f2d3eaeSJakob Unterwurzacher 	struct urb *urb;
11189f2d3eaeSJakob Unterwurzacher 	struct ucan_urb_context *context;
11199f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up = netdev_priv(netdev);
11209f2d3eaeSJakob Unterwurzacher 	struct can_frame *cf = (struct can_frame *)skb->data;
11219f2d3eaeSJakob Unterwurzacher 
11229f2d3eaeSJakob Unterwurzacher 	/* check skb */
1123ae64438bSOliver Hartkopp 	if (can_dev_dropped_skb(netdev, skb))
11249f2d3eaeSJakob Unterwurzacher 		return NETDEV_TX_OK;
11259f2d3eaeSJakob Unterwurzacher 
11269f2d3eaeSJakob Unterwurzacher 	/* allocate a context and slow down tx path, if fifo state is low */
11279f2d3eaeSJakob Unterwurzacher 	context = ucan_alloc_context(up);
11289f2d3eaeSJakob Unterwurzacher 	echo_index = context - up->context_array;
11299f2d3eaeSJakob Unterwurzacher 
11309f2d3eaeSJakob Unterwurzacher 	if (WARN_ON_ONCE(!context))
11319f2d3eaeSJakob Unterwurzacher 		return NETDEV_TX_BUSY;
11329f2d3eaeSJakob Unterwurzacher 
11339f2d3eaeSJakob Unterwurzacher 	/* prepare urb for transmission */
11349f2d3eaeSJakob Unterwurzacher 	urb = ucan_prepare_tx_urb(up, context, cf, echo_index);
11359f2d3eaeSJakob Unterwurzacher 	if (!urb)
11369f2d3eaeSJakob Unterwurzacher 		goto drop;
11379f2d3eaeSJakob Unterwurzacher 
11389f2d3eaeSJakob Unterwurzacher 	/* put the skb on can loopback stack */
11399f2d3eaeSJakob Unterwurzacher 	spin_lock_irqsave(&up->echo_skb_lock, flags);
11401dcb6e57SVincent Mailhol 	can_put_echo_skb(skb, up->netdev, echo_index, 0);
11419f2d3eaeSJakob Unterwurzacher 	spin_unlock_irqrestore(&up->echo_skb_lock, flags);
11429f2d3eaeSJakob Unterwurzacher 
11439f2d3eaeSJakob Unterwurzacher 	/* transmit it */
11449f2d3eaeSJakob Unterwurzacher 	usb_anchor_urb(urb, &up->tx_urbs);
11459f2d3eaeSJakob Unterwurzacher 	ret = usb_submit_urb(urb, GFP_ATOMIC);
11469f2d3eaeSJakob Unterwurzacher 
11479f2d3eaeSJakob Unterwurzacher 	/* cleanup urb */
11489f2d3eaeSJakob Unterwurzacher 	if (ret) {
11499f2d3eaeSJakob Unterwurzacher 		/* on error, clean up */
11509f2d3eaeSJakob Unterwurzacher 		usb_unanchor_urb(urb);
11519f2d3eaeSJakob Unterwurzacher 		ucan_clean_up_tx_urb(up, urb);
11529f2d3eaeSJakob Unterwurzacher 		if (!ucan_release_context(up, context))
11539f2d3eaeSJakob Unterwurzacher 			netdev_err(up->netdev,
11549f2d3eaeSJakob Unterwurzacher 				   "xmit err: failed to release context\n");
11559f2d3eaeSJakob Unterwurzacher 
11569f2d3eaeSJakob Unterwurzacher 		/* remove the skb from the echo stack - this also
11579f2d3eaeSJakob Unterwurzacher 		 * frees the skb
11589f2d3eaeSJakob Unterwurzacher 		 */
11599f2d3eaeSJakob Unterwurzacher 		spin_lock_irqsave(&up->echo_skb_lock, flags);
1160f318482aSMarc Kleine-Budde 		can_free_echo_skb(up->netdev, echo_index, NULL);
11619f2d3eaeSJakob Unterwurzacher 		spin_unlock_irqrestore(&up->echo_skb_lock, flags);
11629f2d3eaeSJakob Unterwurzacher 
11639f2d3eaeSJakob Unterwurzacher 		if (ret == -ENODEV) {
11649f2d3eaeSJakob Unterwurzacher 			netif_device_detach(up->netdev);
11659f2d3eaeSJakob Unterwurzacher 		} else {
11669f2d3eaeSJakob Unterwurzacher 			netdev_warn(up->netdev,
11679f2d3eaeSJakob Unterwurzacher 				    "xmit err: failed to submit urb %d\n",
11689f2d3eaeSJakob Unterwurzacher 				    ret);
11699f2d3eaeSJakob Unterwurzacher 			up->netdev->stats.tx_dropped++;
11709f2d3eaeSJakob Unterwurzacher 		}
11719f2d3eaeSJakob Unterwurzacher 		return NETDEV_TX_OK;
11729f2d3eaeSJakob Unterwurzacher 	}
11739f2d3eaeSJakob Unterwurzacher 
11749f2d3eaeSJakob Unterwurzacher 	netif_trans_update(netdev);
11759f2d3eaeSJakob Unterwurzacher 
11769f2d3eaeSJakob Unterwurzacher 	/* release ref, as we do not need the urb anymore */
11779f2d3eaeSJakob Unterwurzacher 	usb_free_urb(urb);
11789f2d3eaeSJakob Unterwurzacher 
11799f2d3eaeSJakob Unterwurzacher 	return NETDEV_TX_OK;
11809f2d3eaeSJakob Unterwurzacher 
11819f2d3eaeSJakob Unterwurzacher drop:
11829f2d3eaeSJakob Unterwurzacher 	if (!ucan_release_context(up, context))
11839f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev,
11849f2d3eaeSJakob Unterwurzacher 			   "xmit drop: failed to release context\n");
11859f2d3eaeSJakob Unterwurzacher 	dev_kfree_skb(skb);
11869f2d3eaeSJakob Unterwurzacher 	up->netdev->stats.tx_dropped++;
11879f2d3eaeSJakob Unterwurzacher 
11889f2d3eaeSJakob Unterwurzacher 	return NETDEV_TX_OK;
11899f2d3eaeSJakob Unterwurzacher }
11909f2d3eaeSJakob Unterwurzacher 
11919f2d3eaeSJakob Unterwurzacher /* Device goes down
11929f2d3eaeSJakob Unterwurzacher  *
11939f2d3eaeSJakob Unterwurzacher  * Clean up used resources
11949f2d3eaeSJakob Unterwurzacher  */
ucan_close(struct net_device * netdev)11959f2d3eaeSJakob Unterwurzacher static int ucan_close(struct net_device *netdev)
11969f2d3eaeSJakob Unterwurzacher {
11979f2d3eaeSJakob Unterwurzacher 	int ret;
11989f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up = netdev_priv(netdev);
11999f2d3eaeSJakob Unterwurzacher 
12009f2d3eaeSJakob Unterwurzacher 	up->can.state = CAN_STATE_STOPPED;
12019f2d3eaeSJakob Unterwurzacher 
12029f2d3eaeSJakob Unterwurzacher 	/* stop sending data */
12039f2d3eaeSJakob Unterwurzacher 	usb_kill_anchored_urbs(&up->tx_urbs);
12049f2d3eaeSJakob Unterwurzacher 
12059f2d3eaeSJakob Unterwurzacher 	/* stop receiving data */
12069f2d3eaeSJakob Unterwurzacher 	usb_kill_anchored_urbs(&up->rx_urbs);
12079f2d3eaeSJakob Unterwurzacher 
12089f2d3eaeSJakob Unterwurzacher 	/* stop and reset can device */
12099f2d3eaeSJakob Unterwurzacher 	ret = ucan_ctrl_command_out(up, UCAN_COMMAND_STOP, 0, 0);
12109f2d3eaeSJakob Unterwurzacher 	if (ret < 0)
12119f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev,
12129f2d3eaeSJakob Unterwurzacher 			   "could not stop device, code: %d\n",
12139f2d3eaeSJakob Unterwurzacher 			   ret);
12149f2d3eaeSJakob Unterwurzacher 
12159f2d3eaeSJakob Unterwurzacher 	ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0);
12169f2d3eaeSJakob Unterwurzacher 	if (ret < 0)
12179f2d3eaeSJakob Unterwurzacher 		netdev_err(up->netdev,
12189f2d3eaeSJakob Unterwurzacher 			   "could not reset device, code: %d\n",
12199f2d3eaeSJakob Unterwurzacher 			   ret);
12209f2d3eaeSJakob Unterwurzacher 
12219f2d3eaeSJakob Unterwurzacher 	netif_stop_queue(netdev);
12229f2d3eaeSJakob Unterwurzacher 
12239f2d3eaeSJakob Unterwurzacher 	ucan_release_context_array(up);
12249f2d3eaeSJakob Unterwurzacher 
12259f2d3eaeSJakob Unterwurzacher 	close_candev(up->netdev);
12269f2d3eaeSJakob Unterwurzacher 	return 0;
12279f2d3eaeSJakob Unterwurzacher }
12289f2d3eaeSJakob Unterwurzacher 
12299f2d3eaeSJakob Unterwurzacher /* CAN driver callbacks */
12309f2d3eaeSJakob Unterwurzacher static const struct net_device_ops ucan_netdev_ops = {
12319f2d3eaeSJakob Unterwurzacher 	.ndo_open = ucan_open,
12329f2d3eaeSJakob Unterwurzacher 	.ndo_stop = ucan_close,
12339f2d3eaeSJakob Unterwurzacher 	.ndo_start_xmit = ucan_start_xmit,
12349f2d3eaeSJakob Unterwurzacher 	.ndo_change_mtu = can_change_mtu,
12359f2d3eaeSJakob Unterwurzacher };
12369f2d3eaeSJakob Unterwurzacher 
1237409c188cSVincent Mailhol static const struct ethtool_ops ucan_ethtool_ops = {
1238409c188cSVincent Mailhol 	.get_ts_info = ethtool_op_get_ts_info,
1239409c188cSVincent Mailhol };
1240409c188cSVincent Mailhol 
12419f2d3eaeSJakob Unterwurzacher /* Request to set bittiming
12429f2d3eaeSJakob Unterwurzacher  *
12439f2d3eaeSJakob Unterwurzacher  * This function generates an USB set bittiming message and transmits
12449f2d3eaeSJakob Unterwurzacher  * it to the device
12459f2d3eaeSJakob Unterwurzacher  */
ucan_set_bittiming(struct net_device * netdev)12469f2d3eaeSJakob Unterwurzacher static int ucan_set_bittiming(struct net_device *netdev)
12479f2d3eaeSJakob Unterwurzacher {
12489f2d3eaeSJakob Unterwurzacher 	int ret;
12499f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up = netdev_priv(netdev);
12509f2d3eaeSJakob Unterwurzacher 	struct ucan_ctl_cmd_set_bittiming *cmd_set_bittiming;
12519f2d3eaeSJakob Unterwurzacher 
12529f2d3eaeSJakob Unterwurzacher 	cmd_set_bittiming = &up->ctl_msg_buffer->cmd_set_bittiming;
12539f2d3eaeSJakob Unterwurzacher 	cmd_set_bittiming->tq = cpu_to_le32(up->can.bittiming.tq);
12549f2d3eaeSJakob Unterwurzacher 	cmd_set_bittiming->brp = cpu_to_le16(up->can.bittiming.brp);
12559f2d3eaeSJakob Unterwurzacher 	cmd_set_bittiming->sample_point =
12569f2d3eaeSJakob Unterwurzacher 	    cpu_to_le16(up->can.bittiming.sample_point);
12579f2d3eaeSJakob Unterwurzacher 	cmd_set_bittiming->prop_seg = up->can.bittiming.prop_seg;
12589f2d3eaeSJakob Unterwurzacher 	cmd_set_bittiming->phase_seg1 = up->can.bittiming.phase_seg1;
12599f2d3eaeSJakob Unterwurzacher 	cmd_set_bittiming->phase_seg2 = up->can.bittiming.phase_seg2;
12609f2d3eaeSJakob Unterwurzacher 	cmd_set_bittiming->sjw = up->can.bittiming.sjw;
12619f2d3eaeSJakob Unterwurzacher 
12629f2d3eaeSJakob Unterwurzacher 	ret = ucan_ctrl_command_out(up, UCAN_COMMAND_SET_BITTIMING, 0,
12639f2d3eaeSJakob Unterwurzacher 				    sizeof(*cmd_set_bittiming));
12649f2d3eaeSJakob Unterwurzacher 	return (ret < 0) ? ret : 0;
12659f2d3eaeSJakob Unterwurzacher }
12669f2d3eaeSJakob Unterwurzacher 
12679f2d3eaeSJakob Unterwurzacher /* Restart the device to get it out of BUS-OFF state.
12689f2d3eaeSJakob Unterwurzacher  * Called when the user runs "ip link set can1 type can restart".
12699f2d3eaeSJakob Unterwurzacher  */
ucan_set_mode(struct net_device * netdev,enum can_mode mode)12709f2d3eaeSJakob Unterwurzacher static int ucan_set_mode(struct net_device *netdev, enum can_mode mode)
12719f2d3eaeSJakob Unterwurzacher {
12729f2d3eaeSJakob Unterwurzacher 	int ret;
12739f2d3eaeSJakob Unterwurzacher 	unsigned long flags;
12749f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up = netdev_priv(netdev);
12759f2d3eaeSJakob Unterwurzacher 
12769f2d3eaeSJakob Unterwurzacher 	switch (mode) {
12779f2d3eaeSJakob Unterwurzacher 	case CAN_MODE_START:
12789f2d3eaeSJakob Unterwurzacher 		netdev_dbg(up->netdev, "restarting device\n");
12799f2d3eaeSJakob Unterwurzacher 
12809f2d3eaeSJakob Unterwurzacher 		ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESTART, 0, 0);
12819f2d3eaeSJakob Unterwurzacher 		up->can.state = CAN_STATE_ERROR_ACTIVE;
12829f2d3eaeSJakob Unterwurzacher 
12839f2d3eaeSJakob Unterwurzacher 		/* check if queue can be restarted,
12849f2d3eaeSJakob Unterwurzacher 		 * up->available_tx_urbs must be protected by the
12859f2d3eaeSJakob Unterwurzacher 		 * lock
12869f2d3eaeSJakob Unterwurzacher 		 */
12879f2d3eaeSJakob Unterwurzacher 		spin_lock_irqsave(&up->context_lock, flags);
12889f2d3eaeSJakob Unterwurzacher 
12899f2d3eaeSJakob Unterwurzacher 		if (up->available_tx_urbs > 0)
12909f2d3eaeSJakob Unterwurzacher 			netif_wake_queue(up->netdev);
12919f2d3eaeSJakob Unterwurzacher 
12929f2d3eaeSJakob Unterwurzacher 		spin_unlock_irqrestore(&up->context_lock, flags);
12939f2d3eaeSJakob Unterwurzacher 
12949f2d3eaeSJakob Unterwurzacher 		return ret;
12959f2d3eaeSJakob Unterwurzacher 	default:
12969f2d3eaeSJakob Unterwurzacher 		return -EOPNOTSUPP;
12979f2d3eaeSJakob Unterwurzacher 	}
12989f2d3eaeSJakob Unterwurzacher }
12999f2d3eaeSJakob Unterwurzacher 
13009f2d3eaeSJakob Unterwurzacher /* Probe the device, reset it and gather general device information */
ucan_probe(struct usb_interface * intf,const struct usb_device_id * id)13019f2d3eaeSJakob Unterwurzacher static int ucan_probe(struct usb_interface *intf,
13029f2d3eaeSJakob Unterwurzacher 		      const struct usb_device_id *id)
13039f2d3eaeSJakob Unterwurzacher {
13049f2d3eaeSJakob Unterwurzacher 	int ret;
13059f2d3eaeSJakob Unterwurzacher 	int i;
13069f2d3eaeSJakob Unterwurzacher 	u32 protocol_version;
13079f2d3eaeSJakob Unterwurzacher 	struct usb_device *udev;
13089f2d3eaeSJakob Unterwurzacher 	struct net_device *netdev;
13099f2d3eaeSJakob Unterwurzacher 	struct usb_host_interface *iface_desc;
13109f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up;
13119f2d3eaeSJakob Unterwurzacher 	struct usb_endpoint_descriptor *ep;
13129f2d3eaeSJakob Unterwurzacher 	u16 in_ep_size;
13139f2d3eaeSJakob Unterwurzacher 	u16 out_ep_size;
13149f2d3eaeSJakob Unterwurzacher 	u8 in_ep_addr;
13159f2d3eaeSJakob Unterwurzacher 	u8 out_ep_addr;
13169f2d3eaeSJakob Unterwurzacher 	union ucan_ctl_payload *ctl_msg_buffer;
13179f2d3eaeSJakob Unterwurzacher 	char firmware_str[sizeof(union ucan_ctl_payload) + 1];
13189f2d3eaeSJakob Unterwurzacher 
13199f2d3eaeSJakob Unterwurzacher 	udev = interface_to_usbdev(intf);
13209f2d3eaeSJakob Unterwurzacher 
13219f2d3eaeSJakob Unterwurzacher 	/* Stage 1 - Interface Parsing
13229f2d3eaeSJakob Unterwurzacher 	 * ---------------------------
13239f2d3eaeSJakob Unterwurzacher 	 *
13249f2d3eaeSJakob Unterwurzacher 	 * Identifie the device USB interface descriptor and its
13259f2d3eaeSJakob Unterwurzacher 	 * endpoints. Probing is aborted on errors.
13269f2d3eaeSJakob Unterwurzacher 	 */
13279f2d3eaeSJakob Unterwurzacher 
13289f2d3eaeSJakob Unterwurzacher 	/* check if the interface is sane */
13299f2d3eaeSJakob Unterwurzacher 	iface_desc = intf->cur_altsetting;
13309f2d3eaeSJakob Unterwurzacher 	if (!iface_desc)
13319f2d3eaeSJakob Unterwurzacher 		return -ENODEV;
13329f2d3eaeSJakob Unterwurzacher 
13339f2d3eaeSJakob Unterwurzacher 	dev_info(&udev->dev,
13349f2d3eaeSJakob Unterwurzacher 		 "%s: probing device on interface #%d\n",
13359f2d3eaeSJakob Unterwurzacher 		 UCAN_DRIVER_NAME,
13369f2d3eaeSJakob Unterwurzacher 		 iface_desc->desc.bInterfaceNumber);
13379f2d3eaeSJakob Unterwurzacher 
13389f2d3eaeSJakob Unterwurzacher 	/* interface sanity check */
13399f2d3eaeSJakob Unterwurzacher 	if (iface_desc->desc.bNumEndpoints != 2) {
13409f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev,
13419f2d3eaeSJakob Unterwurzacher 			"%s: invalid EP count (%d)",
13429f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME, iface_desc->desc.bNumEndpoints);
13439f2d3eaeSJakob Unterwurzacher 		goto err_firmware_needs_update;
13449f2d3eaeSJakob Unterwurzacher 	}
13459f2d3eaeSJakob Unterwurzacher 
13469f2d3eaeSJakob Unterwurzacher 	/* check interface endpoints */
13479f2d3eaeSJakob Unterwurzacher 	in_ep_addr = 0;
13489f2d3eaeSJakob Unterwurzacher 	out_ep_addr = 0;
13499f2d3eaeSJakob Unterwurzacher 	in_ep_size = 0;
13509f2d3eaeSJakob Unterwurzacher 	out_ep_size = 0;
13519f2d3eaeSJakob Unterwurzacher 	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
13529f2d3eaeSJakob Unterwurzacher 		ep = &iface_desc->endpoint[i].desc;
13539f2d3eaeSJakob Unterwurzacher 
13549f2d3eaeSJakob Unterwurzacher 		if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0) &&
13559f2d3eaeSJakob Unterwurzacher 		    ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
13569f2d3eaeSJakob Unterwurzacher 		     USB_ENDPOINT_XFER_BULK)) {
13579f2d3eaeSJakob Unterwurzacher 			/* In Endpoint */
13589f2d3eaeSJakob Unterwurzacher 			in_ep_addr = ep->bEndpointAddress;
13599f2d3eaeSJakob Unterwurzacher 			in_ep_addr &= USB_ENDPOINT_NUMBER_MASK;
13609f2d3eaeSJakob Unterwurzacher 			in_ep_size = le16_to_cpu(ep->wMaxPacketSize);
13619f2d3eaeSJakob Unterwurzacher 		} else if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
13629f2d3eaeSJakob Unterwurzacher 			    0) &&
13639f2d3eaeSJakob Unterwurzacher 			   ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
13649f2d3eaeSJakob Unterwurzacher 			    USB_ENDPOINT_XFER_BULK)) {
13659f2d3eaeSJakob Unterwurzacher 			/* Out Endpoint */
13669f2d3eaeSJakob Unterwurzacher 			out_ep_addr = ep->bEndpointAddress;
13679f2d3eaeSJakob Unterwurzacher 			out_ep_addr &= USB_ENDPOINT_NUMBER_MASK;
13689f2d3eaeSJakob Unterwurzacher 			out_ep_size = le16_to_cpu(ep->wMaxPacketSize);
13699f2d3eaeSJakob Unterwurzacher 		}
13709f2d3eaeSJakob Unterwurzacher 	}
13719f2d3eaeSJakob Unterwurzacher 
13729f2d3eaeSJakob Unterwurzacher 	/* check if interface is sane */
13739f2d3eaeSJakob Unterwurzacher 	if (!in_ep_addr || !out_ep_addr) {
13749f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev, "%s: invalid endpoint configuration\n",
13759f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME);
13769f2d3eaeSJakob Unterwurzacher 		goto err_firmware_needs_update;
13779f2d3eaeSJakob Unterwurzacher 	}
13789f2d3eaeSJakob Unterwurzacher 	if (in_ep_size < sizeof(struct ucan_message_in)) {
13799f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev, "%s: invalid in_ep MaxPacketSize\n",
13809f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME);
13819f2d3eaeSJakob Unterwurzacher 		goto err_firmware_needs_update;
13829f2d3eaeSJakob Unterwurzacher 	}
13839f2d3eaeSJakob Unterwurzacher 	if (out_ep_size < sizeof(struct ucan_message_out)) {
13849f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev, "%s: invalid out_ep MaxPacketSize\n",
13859f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME);
13869f2d3eaeSJakob Unterwurzacher 		goto err_firmware_needs_update;
13879f2d3eaeSJakob Unterwurzacher 	}
13889f2d3eaeSJakob Unterwurzacher 
13899f2d3eaeSJakob Unterwurzacher 	/* Stage 2 - Device Identification
13909f2d3eaeSJakob Unterwurzacher 	 * -------------------------------
13919f2d3eaeSJakob Unterwurzacher 	 *
13929f2d3eaeSJakob Unterwurzacher 	 * The device interface seems to be a ucan device. Do further
13939f2d3eaeSJakob Unterwurzacher 	 * compatibility checks. On error probing is aborted, on
13949f2d3eaeSJakob Unterwurzacher 	 * success this stage leaves the ctl_msg_buffer with the
13959f2d3eaeSJakob Unterwurzacher 	 * reported contents of a GET_INFO command (supported
13969f2d3eaeSJakob Unterwurzacher 	 * bittimings, tx_fifo depth). This information is used in
13979f2d3eaeSJakob Unterwurzacher 	 * Stage 3 for the final driver initialisation.
13989f2d3eaeSJakob Unterwurzacher 	 */
13999f2d3eaeSJakob Unterwurzacher 
1400c34983c9SJulia Lawall 	/* Prepare Memory for control transfers */
14019f2d3eaeSJakob Unterwurzacher 	ctl_msg_buffer = devm_kzalloc(&udev->dev,
14029f2d3eaeSJakob Unterwurzacher 				      sizeof(union ucan_ctl_payload),
14039f2d3eaeSJakob Unterwurzacher 				      GFP_KERNEL);
14049f2d3eaeSJakob Unterwurzacher 	if (!ctl_msg_buffer) {
14059f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev,
14069f2d3eaeSJakob Unterwurzacher 			"%s: failed to allocate control pipe memory\n",
14079f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME);
14089f2d3eaeSJakob Unterwurzacher 		return -ENOMEM;
14099f2d3eaeSJakob Unterwurzacher 	}
14109f2d3eaeSJakob Unterwurzacher 
14119f2d3eaeSJakob Unterwurzacher 	/* get protocol version
14129f2d3eaeSJakob Unterwurzacher 	 *
14139f2d3eaeSJakob Unterwurzacher 	 * note: ucan_ctrl_command_* wrappers cannot be used yet
14149f2d3eaeSJakob Unterwurzacher 	 * because `up` is initialised in Stage 3
14159f2d3eaeSJakob Unterwurzacher 	 */
14169f2d3eaeSJakob Unterwurzacher 	ret = usb_control_msg(udev,
14179f2d3eaeSJakob Unterwurzacher 			      usb_rcvctrlpipe(udev, 0),
14189f2d3eaeSJakob Unterwurzacher 			      UCAN_COMMAND_GET,
14199f2d3eaeSJakob Unterwurzacher 			      USB_DIR_IN | USB_TYPE_VENDOR |
14209f2d3eaeSJakob Unterwurzacher 					USB_RECIP_INTERFACE,
14219f2d3eaeSJakob Unterwurzacher 			      UCAN_COMMAND_GET_PROTOCOL_VERSION,
14229f2d3eaeSJakob Unterwurzacher 			      iface_desc->desc.bInterfaceNumber,
14239f2d3eaeSJakob Unterwurzacher 			      ctl_msg_buffer,
14249f2d3eaeSJakob Unterwurzacher 			      sizeof(union ucan_ctl_payload),
14259f2d3eaeSJakob Unterwurzacher 			      UCAN_USB_CTL_PIPE_TIMEOUT);
14269f2d3eaeSJakob Unterwurzacher 
14279f2d3eaeSJakob Unterwurzacher 	/* older firmware version do not support this command - those
14289f2d3eaeSJakob Unterwurzacher 	 * are not supported by this drive
14299f2d3eaeSJakob Unterwurzacher 	 */
14309f2d3eaeSJakob Unterwurzacher 	if (ret != 4) {
14319f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev,
14329f2d3eaeSJakob Unterwurzacher 			"%s: could not read protocol version, ret=%d\n",
14339f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME, ret);
14349f2d3eaeSJakob Unterwurzacher 		if (ret >= 0)
14359f2d3eaeSJakob Unterwurzacher 			ret = -EINVAL;
14369f2d3eaeSJakob Unterwurzacher 		goto err_firmware_needs_update;
14379f2d3eaeSJakob Unterwurzacher 	}
14389f2d3eaeSJakob Unterwurzacher 
14399f2d3eaeSJakob Unterwurzacher 	/* this driver currently supports protocol version 3 only */
14409f2d3eaeSJakob Unterwurzacher 	protocol_version =
14419f2d3eaeSJakob Unterwurzacher 		le32_to_cpu(ctl_msg_buffer->cmd_get_protocol_version.version);
14429f2d3eaeSJakob Unterwurzacher 	if (protocol_version < UCAN_PROTOCOL_VERSION_MIN ||
14439f2d3eaeSJakob Unterwurzacher 	    protocol_version > UCAN_PROTOCOL_VERSION_MAX) {
14449f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev,
14459f2d3eaeSJakob Unterwurzacher 			"%s: device protocol version %d is not supported\n",
14469f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME, protocol_version);
14479f2d3eaeSJakob Unterwurzacher 		goto err_firmware_needs_update;
14489f2d3eaeSJakob Unterwurzacher 	}
14499f2d3eaeSJakob Unterwurzacher 
14509f2d3eaeSJakob Unterwurzacher 	/* request the device information and store it in ctl_msg_buffer
14519f2d3eaeSJakob Unterwurzacher 	 *
145288bfb9a7SMarc Kleine-Budde 	 * note: ucan_ctrl_command_* wrappers cannot be used yet
14539f2d3eaeSJakob Unterwurzacher 	 * because `up` is initialised in Stage 3
14549f2d3eaeSJakob Unterwurzacher 	 */
14559f2d3eaeSJakob Unterwurzacher 	ret = usb_control_msg(udev,
14569f2d3eaeSJakob Unterwurzacher 			      usb_rcvctrlpipe(udev, 0),
14579f2d3eaeSJakob Unterwurzacher 			      UCAN_COMMAND_GET,
14589f2d3eaeSJakob Unterwurzacher 			      USB_DIR_IN | USB_TYPE_VENDOR |
14599f2d3eaeSJakob Unterwurzacher 					USB_RECIP_INTERFACE,
14609f2d3eaeSJakob Unterwurzacher 			      UCAN_COMMAND_GET_INFO,
14619f2d3eaeSJakob Unterwurzacher 			      iface_desc->desc.bInterfaceNumber,
14629f2d3eaeSJakob Unterwurzacher 			      ctl_msg_buffer,
14639f2d3eaeSJakob Unterwurzacher 			      sizeof(ctl_msg_buffer->cmd_get_device_info),
14649f2d3eaeSJakob Unterwurzacher 			      UCAN_USB_CTL_PIPE_TIMEOUT);
14659f2d3eaeSJakob Unterwurzacher 
14669f2d3eaeSJakob Unterwurzacher 	if (ret < 0) {
14679f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev, "%s: failed to retrieve device info\n",
14689f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME);
14699f2d3eaeSJakob Unterwurzacher 		goto err_firmware_needs_update;
14709f2d3eaeSJakob Unterwurzacher 	}
14719f2d3eaeSJakob Unterwurzacher 	if (ret < sizeof(ctl_msg_buffer->cmd_get_device_info)) {
14729f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev, "%s: device reported invalid device info\n",
14739f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME);
14749f2d3eaeSJakob Unterwurzacher 		goto err_firmware_needs_update;
14759f2d3eaeSJakob Unterwurzacher 	}
14769f2d3eaeSJakob Unterwurzacher 	if (ctl_msg_buffer->cmd_get_device_info.tx_fifo == 0) {
14779f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev,
14789f2d3eaeSJakob Unterwurzacher 			"%s: device reported invalid tx-fifo size\n",
14799f2d3eaeSJakob Unterwurzacher 			UCAN_DRIVER_NAME);
14809f2d3eaeSJakob Unterwurzacher 		goto err_firmware_needs_update;
14819f2d3eaeSJakob Unterwurzacher 	}
14829f2d3eaeSJakob Unterwurzacher 
14839f2d3eaeSJakob Unterwurzacher 	/* Stage 3 - Driver Initialisation
14849f2d3eaeSJakob Unterwurzacher 	 * -------------------------------
14859f2d3eaeSJakob Unterwurzacher 	 *
14869f2d3eaeSJakob Unterwurzacher 	 * Register device to Linux, prepare private structures and
14879f2d3eaeSJakob Unterwurzacher 	 * reset the device.
14889f2d3eaeSJakob Unterwurzacher 	 */
14899f2d3eaeSJakob Unterwurzacher 
14909f2d3eaeSJakob Unterwurzacher 	/* allocate driver resources */
14919f2d3eaeSJakob Unterwurzacher 	netdev = alloc_candev(sizeof(struct ucan_priv),
14929f2d3eaeSJakob Unterwurzacher 			      ctl_msg_buffer->cmd_get_device_info.tx_fifo);
14939f2d3eaeSJakob Unterwurzacher 	if (!netdev) {
14949f2d3eaeSJakob Unterwurzacher 		dev_err(&udev->dev,
14959f2d3eaeSJakob Unterwurzacher 			"%s: cannot allocate candev\n", UCAN_DRIVER_NAME);
14969f2d3eaeSJakob Unterwurzacher 		return -ENOMEM;
14979f2d3eaeSJakob Unterwurzacher 	}
14989f2d3eaeSJakob Unterwurzacher 
14999f2d3eaeSJakob Unterwurzacher 	up = netdev_priv(netdev);
15009f2d3eaeSJakob Unterwurzacher 
150188bfb9a7SMarc Kleine-Budde 	/* initialize data */
15029f2d3eaeSJakob Unterwurzacher 	up->udev = udev;
15039f2d3eaeSJakob Unterwurzacher 	up->netdev = netdev;
15049f2d3eaeSJakob Unterwurzacher 	up->intf_index = iface_desc->desc.bInterfaceNumber;
15059f2d3eaeSJakob Unterwurzacher 	up->in_ep_addr = in_ep_addr;
15069f2d3eaeSJakob Unterwurzacher 	up->out_ep_addr = out_ep_addr;
15079f2d3eaeSJakob Unterwurzacher 	up->in_ep_size = in_ep_size;
15089f2d3eaeSJakob Unterwurzacher 	up->ctl_msg_buffer = ctl_msg_buffer;
15099f2d3eaeSJakob Unterwurzacher 	up->context_array = NULL;
15109f2d3eaeSJakob Unterwurzacher 	up->available_tx_urbs = 0;
15119f2d3eaeSJakob Unterwurzacher 
15129f2d3eaeSJakob Unterwurzacher 	up->can.state = CAN_STATE_STOPPED;
15139f2d3eaeSJakob Unterwurzacher 	up->can.bittiming_const = &up->device_info.bittiming_const;
15149f2d3eaeSJakob Unterwurzacher 	up->can.do_set_bittiming = ucan_set_bittiming;
15159f2d3eaeSJakob Unterwurzacher 	up->can.do_set_mode = &ucan_set_mode;
15169f2d3eaeSJakob Unterwurzacher 	spin_lock_init(&up->context_lock);
15179f2d3eaeSJakob Unterwurzacher 	spin_lock_init(&up->echo_skb_lock);
15189f2d3eaeSJakob Unterwurzacher 	netdev->netdev_ops = &ucan_netdev_ops;
1519409c188cSVincent Mailhol 	netdev->ethtool_ops = &ucan_ethtool_ops;
15209f2d3eaeSJakob Unterwurzacher 
15219f2d3eaeSJakob Unterwurzacher 	usb_set_intfdata(intf, up);
15229f2d3eaeSJakob Unterwurzacher 	SET_NETDEV_DEV(netdev, &intf->dev);
15239f2d3eaeSJakob Unterwurzacher 
15249f2d3eaeSJakob Unterwurzacher 	/* parse device information
15259f2d3eaeSJakob Unterwurzacher 	 * the data retrieved in Stage 2 is still available in
15269f2d3eaeSJakob Unterwurzacher 	 * up->ctl_msg_buffer
15279f2d3eaeSJakob Unterwurzacher 	 */
15289f2d3eaeSJakob Unterwurzacher 	ucan_parse_device_info(up, &ctl_msg_buffer->cmd_get_device_info);
15299f2d3eaeSJakob Unterwurzacher 
15309f2d3eaeSJakob Unterwurzacher 	/* just print some device information - if available */
15319f2d3eaeSJakob Unterwurzacher 	ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0,
15329f2d3eaeSJakob Unterwurzacher 				     sizeof(union ucan_ctl_payload));
15339f2d3eaeSJakob Unterwurzacher 	if (ret > 0) {
1534c34983c9SJulia Lawall 		/* copy string while ensuring zero termination */
15357fdaf896SXu Panda 		strscpy(firmware_str, up->ctl_msg_buffer->raw,
15367fdaf896SXu Panda 			sizeof(union ucan_ctl_payload) + 1);
15379f2d3eaeSJakob Unterwurzacher 	} else {
15389f2d3eaeSJakob Unterwurzacher 		strcpy(firmware_str, "unknown");
15399f2d3eaeSJakob Unterwurzacher 	}
15409f2d3eaeSJakob Unterwurzacher 
15419f2d3eaeSJakob Unterwurzacher 	/* device is compatible, reset it */
15429f2d3eaeSJakob Unterwurzacher 	ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0);
15439f2d3eaeSJakob Unterwurzacher 	if (ret < 0)
15449f2d3eaeSJakob Unterwurzacher 		goto err_free_candev;
15459f2d3eaeSJakob Unterwurzacher 
15469f2d3eaeSJakob Unterwurzacher 	init_usb_anchor(&up->rx_urbs);
15479f2d3eaeSJakob Unterwurzacher 	init_usb_anchor(&up->tx_urbs);
15489f2d3eaeSJakob Unterwurzacher 
15499f2d3eaeSJakob Unterwurzacher 	up->can.state = CAN_STATE_STOPPED;
15509f2d3eaeSJakob Unterwurzacher 
15519f2d3eaeSJakob Unterwurzacher 	/* register the device */
15529f2d3eaeSJakob Unterwurzacher 	ret = register_candev(netdev);
15539f2d3eaeSJakob Unterwurzacher 	if (ret)
15549f2d3eaeSJakob Unterwurzacher 		goto err_free_candev;
15559f2d3eaeSJakob Unterwurzacher 
15569f2d3eaeSJakob Unterwurzacher 	/* initialisation complete, log device info */
15579f2d3eaeSJakob Unterwurzacher 	netdev_info(up->netdev, "registered device\n");
15589f2d3eaeSJakob Unterwurzacher 	netdev_info(up->netdev, "firmware string: %s\n", firmware_str);
15599f2d3eaeSJakob Unterwurzacher 
15609f2d3eaeSJakob Unterwurzacher 	/* success */
15619f2d3eaeSJakob Unterwurzacher 	return 0;
15629f2d3eaeSJakob Unterwurzacher 
15639f2d3eaeSJakob Unterwurzacher err_free_candev:
15649f2d3eaeSJakob Unterwurzacher 	free_candev(netdev);
15659f2d3eaeSJakob Unterwurzacher 	return ret;
15669f2d3eaeSJakob Unterwurzacher 
15679f2d3eaeSJakob Unterwurzacher err_firmware_needs_update:
15689f2d3eaeSJakob Unterwurzacher 	dev_err(&udev->dev,
15699f2d3eaeSJakob Unterwurzacher 		"%s: probe failed; try to update the device firmware\n",
15709f2d3eaeSJakob Unterwurzacher 		UCAN_DRIVER_NAME);
15719f2d3eaeSJakob Unterwurzacher 	return -ENODEV;
15729f2d3eaeSJakob Unterwurzacher }
15739f2d3eaeSJakob Unterwurzacher 
15749f2d3eaeSJakob Unterwurzacher /* disconnect the device */
ucan_disconnect(struct usb_interface * intf)15759f2d3eaeSJakob Unterwurzacher static void ucan_disconnect(struct usb_interface *intf)
15769f2d3eaeSJakob Unterwurzacher {
15779f2d3eaeSJakob Unterwurzacher 	struct ucan_priv *up = usb_get_intfdata(intf);
15789f2d3eaeSJakob Unterwurzacher 
15799f2d3eaeSJakob Unterwurzacher 	usb_set_intfdata(intf, NULL);
15809f2d3eaeSJakob Unterwurzacher 
15819f2d3eaeSJakob Unterwurzacher 	if (up) {
1582aa9832e4SDongliang Mu 		unregister_candev(up->netdev);
15839f2d3eaeSJakob Unterwurzacher 		free_candev(up->netdev);
15849f2d3eaeSJakob Unterwurzacher 	}
15859f2d3eaeSJakob Unterwurzacher }
15869f2d3eaeSJakob Unterwurzacher 
15879f2d3eaeSJakob Unterwurzacher static struct usb_device_id ucan_table[] = {
15889f2d3eaeSJakob Unterwurzacher 	/* Mule (soldered onto compute modules) */
15899f2d3eaeSJakob Unterwurzacher 	{USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425a, 0)},
15909f2d3eaeSJakob Unterwurzacher 	/* Seal (standalone USB stick) */
15919f2d3eaeSJakob Unterwurzacher 	{USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425b, 0)},
15929f2d3eaeSJakob Unterwurzacher 	{} /* Terminating entry */
15939f2d3eaeSJakob Unterwurzacher };
15949f2d3eaeSJakob Unterwurzacher 
15959f2d3eaeSJakob Unterwurzacher MODULE_DEVICE_TABLE(usb, ucan_table);
15969f2d3eaeSJakob Unterwurzacher /* driver callbacks */
15979f2d3eaeSJakob Unterwurzacher static struct usb_driver ucan_driver = {
15989f2d3eaeSJakob Unterwurzacher 	.name = UCAN_DRIVER_NAME,
15999f2d3eaeSJakob Unterwurzacher 	.probe = ucan_probe,
16009f2d3eaeSJakob Unterwurzacher 	.disconnect = ucan_disconnect,
16019f2d3eaeSJakob Unterwurzacher 	.id_table = ucan_table,
16029f2d3eaeSJakob Unterwurzacher };
16039f2d3eaeSJakob Unterwurzacher 
16049f2d3eaeSJakob Unterwurzacher module_usb_driver(ucan_driver);
16059f2d3eaeSJakob Unterwurzacher 
16069f2d3eaeSJakob Unterwurzacher MODULE_LICENSE("GPL v2");
16079f2d3eaeSJakob Unterwurzacher MODULE_AUTHOR("Martin Elshuber <martin.elshuber@theobroma-systems.com>");
16089f2d3eaeSJakob Unterwurzacher MODULE_AUTHOR("Jakob Unterwurzacher <jakob.unterwurzacher@theobroma-systems.com>");
16099f2d3eaeSJakob Unterwurzacher MODULE_DESCRIPTION("Driver for Theobroma Systems UCAN devices");
1610