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