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 usb_interface *intf; 2819f2d3eaeSJakob Unterwurzacher struct net_device *netdev; 2829f2d3eaeSJakob Unterwurzacher 2839f2d3eaeSJakob Unterwurzacher /* lock for can->echo_skb (used around 2849f2d3eaeSJakob Unterwurzacher * can_put/get/free_echo_skb 2859f2d3eaeSJakob Unterwurzacher */ 2869f2d3eaeSJakob Unterwurzacher spinlock_t echo_skb_lock; 2879f2d3eaeSJakob Unterwurzacher 2889f2d3eaeSJakob Unterwurzacher /* usb device information information */ 2899f2d3eaeSJakob Unterwurzacher u8 intf_index; 2909f2d3eaeSJakob Unterwurzacher u8 in_ep_addr; 2919f2d3eaeSJakob Unterwurzacher u8 out_ep_addr; 2929f2d3eaeSJakob Unterwurzacher u16 in_ep_size; 2939f2d3eaeSJakob Unterwurzacher 2949f2d3eaeSJakob Unterwurzacher /* transmission and reception buffers */ 2959f2d3eaeSJakob Unterwurzacher struct usb_anchor rx_urbs; 2969f2d3eaeSJakob Unterwurzacher struct usb_anchor tx_urbs; 2979f2d3eaeSJakob Unterwurzacher 2989f2d3eaeSJakob Unterwurzacher union ucan_ctl_payload *ctl_msg_buffer; 2999f2d3eaeSJakob Unterwurzacher struct ucan_device_info device_info; 3009f2d3eaeSJakob Unterwurzacher 3019f2d3eaeSJakob Unterwurzacher /* transmission control information and locks */ 3029f2d3eaeSJakob Unterwurzacher spinlock_t context_lock; 3039f2d3eaeSJakob Unterwurzacher unsigned int available_tx_urbs; 3049f2d3eaeSJakob Unterwurzacher struct ucan_urb_context *context_array; 3059f2d3eaeSJakob Unterwurzacher }; 3069f2d3eaeSJakob Unterwurzacher 30769d98969SOliver Hartkopp static u8 ucan_can_cc_dlc2len(struct ucan_can_msg *msg, u16 len) 3089f2d3eaeSJakob Unterwurzacher { 3099f2d3eaeSJakob Unterwurzacher if (le32_to_cpu(msg->id) & CAN_RTR_FLAG) 31069d98969SOliver Hartkopp return can_cc_dlc2len(msg->dlc); 3119f2d3eaeSJakob Unterwurzacher else 31269d98969SOliver Hartkopp return can_cc_dlc2len(len - (UCAN_IN_HDR_SIZE + sizeof(msg->id))); 3139f2d3eaeSJakob Unterwurzacher } 3149f2d3eaeSJakob Unterwurzacher 3159f2d3eaeSJakob Unterwurzacher static void ucan_release_context_array(struct ucan_priv *up) 3169f2d3eaeSJakob Unterwurzacher { 3179f2d3eaeSJakob Unterwurzacher if (!up->context_array) 3189f2d3eaeSJakob Unterwurzacher return; 3199f2d3eaeSJakob Unterwurzacher 3209f2d3eaeSJakob Unterwurzacher /* lock is not needed because, driver is currently opening or closing */ 3219f2d3eaeSJakob Unterwurzacher up->available_tx_urbs = 0; 3229f2d3eaeSJakob Unterwurzacher 3239f2d3eaeSJakob Unterwurzacher kfree(up->context_array); 3249f2d3eaeSJakob Unterwurzacher up->context_array = NULL; 3259f2d3eaeSJakob Unterwurzacher } 3269f2d3eaeSJakob Unterwurzacher 3279f2d3eaeSJakob Unterwurzacher static int ucan_alloc_context_array(struct ucan_priv *up) 3289f2d3eaeSJakob Unterwurzacher { 3299f2d3eaeSJakob Unterwurzacher int i; 3309f2d3eaeSJakob Unterwurzacher 3319f2d3eaeSJakob Unterwurzacher /* release contexts if any */ 3329f2d3eaeSJakob Unterwurzacher ucan_release_context_array(up); 3339f2d3eaeSJakob Unterwurzacher 3349f2d3eaeSJakob Unterwurzacher up->context_array = kcalloc(up->device_info.tx_fifo, 3359f2d3eaeSJakob Unterwurzacher sizeof(*up->context_array), 3369f2d3eaeSJakob Unterwurzacher GFP_KERNEL); 3379f2d3eaeSJakob Unterwurzacher if (!up->context_array) { 3389f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 3399f2d3eaeSJakob Unterwurzacher "Not enough memory to allocate tx contexts\n"); 3409f2d3eaeSJakob Unterwurzacher return -ENOMEM; 3419f2d3eaeSJakob Unterwurzacher } 3429f2d3eaeSJakob Unterwurzacher 3439f2d3eaeSJakob Unterwurzacher for (i = 0; i < up->device_info.tx_fifo; i++) { 3449f2d3eaeSJakob Unterwurzacher up->context_array[i].allocated = false; 3459f2d3eaeSJakob Unterwurzacher up->context_array[i].up = up; 3469f2d3eaeSJakob Unterwurzacher } 3479f2d3eaeSJakob Unterwurzacher 3489f2d3eaeSJakob Unterwurzacher /* lock is not needed because, driver is currently opening */ 3499f2d3eaeSJakob Unterwurzacher up->available_tx_urbs = up->device_info.tx_fifo; 3509f2d3eaeSJakob Unterwurzacher 3519f2d3eaeSJakob Unterwurzacher return 0; 3529f2d3eaeSJakob Unterwurzacher } 3539f2d3eaeSJakob Unterwurzacher 3549f2d3eaeSJakob Unterwurzacher static struct ucan_urb_context *ucan_alloc_context(struct ucan_priv *up) 3559f2d3eaeSJakob Unterwurzacher { 3569f2d3eaeSJakob Unterwurzacher int i; 3579f2d3eaeSJakob Unterwurzacher unsigned long flags; 3589f2d3eaeSJakob Unterwurzacher struct ucan_urb_context *ret = NULL; 3599f2d3eaeSJakob Unterwurzacher 3609f2d3eaeSJakob Unterwurzacher if (WARN_ON_ONCE(!up->context_array)) 3619f2d3eaeSJakob Unterwurzacher return NULL; 3629f2d3eaeSJakob Unterwurzacher 3639f2d3eaeSJakob Unterwurzacher /* execute context operation atomically */ 3649f2d3eaeSJakob Unterwurzacher spin_lock_irqsave(&up->context_lock, flags); 3659f2d3eaeSJakob Unterwurzacher 3669f2d3eaeSJakob Unterwurzacher for (i = 0; i < up->device_info.tx_fifo; i++) { 3679f2d3eaeSJakob Unterwurzacher if (!up->context_array[i].allocated) { 3689f2d3eaeSJakob Unterwurzacher /* update context */ 3699f2d3eaeSJakob Unterwurzacher ret = &up->context_array[i]; 3709f2d3eaeSJakob Unterwurzacher up->context_array[i].allocated = true; 3719f2d3eaeSJakob Unterwurzacher 3729f2d3eaeSJakob Unterwurzacher /* stop queue if necessary */ 3739f2d3eaeSJakob Unterwurzacher up->available_tx_urbs--; 3749f2d3eaeSJakob Unterwurzacher if (!up->available_tx_urbs) 3759f2d3eaeSJakob Unterwurzacher netif_stop_queue(up->netdev); 3769f2d3eaeSJakob Unterwurzacher 3779f2d3eaeSJakob Unterwurzacher break; 3789f2d3eaeSJakob Unterwurzacher } 3799f2d3eaeSJakob Unterwurzacher } 3809f2d3eaeSJakob Unterwurzacher 3819f2d3eaeSJakob Unterwurzacher spin_unlock_irqrestore(&up->context_lock, flags); 3829f2d3eaeSJakob Unterwurzacher return ret; 3839f2d3eaeSJakob Unterwurzacher } 3849f2d3eaeSJakob Unterwurzacher 3859f2d3eaeSJakob Unterwurzacher static bool ucan_release_context(struct ucan_priv *up, 3869f2d3eaeSJakob Unterwurzacher struct ucan_urb_context *ctx) 3879f2d3eaeSJakob Unterwurzacher { 3889f2d3eaeSJakob Unterwurzacher unsigned long flags; 3899f2d3eaeSJakob Unterwurzacher bool ret = false; 3909f2d3eaeSJakob Unterwurzacher 3919f2d3eaeSJakob Unterwurzacher if (WARN_ON_ONCE(!up->context_array)) 3929f2d3eaeSJakob Unterwurzacher return false; 3939f2d3eaeSJakob Unterwurzacher 3949f2d3eaeSJakob Unterwurzacher /* execute context operation atomically */ 3959f2d3eaeSJakob Unterwurzacher spin_lock_irqsave(&up->context_lock, flags); 3969f2d3eaeSJakob Unterwurzacher 3979f2d3eaeSJakob Unterwurzacher /* context was not allocated, maybe the device sent garbage */ 3989f2d3eaeSJakob Unterwurzacher if (ctx->allocated) { 3999f2d3eaeSJakob Unterwurzacher ctx->allocated = false; 4009f2d3eaeSJakob Unterwurzacher 4019f2d3eaeSJakob Unterwurzacher /* check if the queue needs to be woken */ 4029f2d3eaeSJakob Unterwurzacher if (!up->available_tx_urbs) 4039f2d3eaeSJakob Unterwurzacher netif_wake_queue(up->netdev); 4049f2d3eaeSJakob Unterwurzacher up->available_tx_urbs++; 4059f2d3eaeSJakob Unterwurzacher 4069f2d3eaeSJakob Unterwurzacher ret = true; 4079f2d3eaeSJakob Unterwurzacher } 4089f2d3eaeSJakob Unterwurzacher 4099f2d3eaeSJakob Unterwurzacher spin_unlock_irqrestore(&up->context_lock, flags); 4109f2d3eaeSJakob Unterwurzacher return ret; 4119f2d3eaeSJakob Unterwurzacher } 4129f2d3eaeSJakob Unterwurzacher 4139f2d3eaeSJakob Unterwurzacher static int ucan_ctrl_command_out(struct ucan_priv *up, 4149f2d3eaeSJakob Unterwurzacher u8 cmd, u16 subcmd, u16 datalen) 4159f2d3eaeSJakob Unterwurzacher { 4169f2d3eaeSJakob Unterwurzacher return usb_control_msg(up->udev, 4179f2d3eaeSJakob Unterwurzacher usb_sndctrlpipe(up->udev, 0), 4189f2d3eaeSJakob Unterwurzacher cmd, 4199f2d3eaeSJakob Unterwurzacher USB_DIR_OUT | USB_TYPE_VENDOR | 4209f2d3eaeSJakob Unterwurzacher USB_RECIP_INTERFACE, 4219f2d3eaeSJakob Unterwurzacher subcmd, 4229f2d3eaeSJakob Unterwurzacher up->intf_index, 4239f2d3eaeSJakob Unterwurzacher up->ctl_msg_buffer, 4249f2d3eaeSJakob Unterwurzacher datalen, 4259f2d3eaeSJakob Unterwurzacher UCAN_USB_CTL_PIPE_TIMEOUT); 4269f2d3eaeSJakob Unterwurzacher } 4279f2d3eaeSJakob Unterwurzacher 4289f2d3eaeSJakob Unterwurzacher static int ucan_device_request_in(struct ucan_priv *up, 4299f2d3eaeSJakob Unterwurzacher u8 cmd, u16 subcmd, u16 datalen) 4309f2d3eaeSJakob Unterwurzacher { 4319f2d3eaeSJakob Unterwurzacher return usb_control_msg(up->udev, 4329f2d3eaeSJakob Unterwurzacher usb_rcvctrlpipe(up->udev, 0), 4339f2d3eaeSJakob Unterwurzacher cmd, 4349f2d3eaeSJakob Unterwurzacher USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4359f2d3eaeSJakob Unterwurzacher subcmd, 4369f2d3eaeSJakob Unterwurzacher 0, 4379f2d3eaeSJakob Unterwurzacher up->ctl_msg_buffer, 4389f2d3eaeSJakob Unterwurzacher datalen, 4399f2d3eaeSJakob Unterwurzacher UCAN_USB_CTL_PIPE_TIMEOUT); 4409f2d3eaeSJakob Unterwurzacher } 4419f2d3eaeSJakob Unterwurzacher 4429f2d3eaeSJakob Unterwurzacher /* Parse the device information structure reported by the device and 4439f2d3eaeSJakob Unterwurzacher * setup private variables accordingly 4449f2d3eaeSJakob Unterwurzacher */ 4459f2d3eaeSJakob Unterwurzacher static void ucan_parse_device_info(struct ucan_priv *up, 4469f2d3eaeSJakob Unterwurzacher struct ucan_ctl_cmd_device_info *device_info) 4479f2d3eaeSJakob Unterwurzacher { 4489f2d3eaeSJakob Unterwurzacher struct can_bittiming_const *bittiming = 4499f2d3eaeSJakob Unterwurzacher &up->device_info.bittiming_const; 4509f2d3eaeSJakob Unterwurzacher u16 ctrlmodes; 4519f2d3eaeSJakob Unterwurzacher 4529f2d3eaeSJakob Unterwurzacher /* store the data */ 4539f2d3eaeSJakob Unterwurzacher up->can.clock.freq = le32_to_cpu(device_info->freq); 4549f2d3eaeSJakob Unterwurzacher up->device_info.tx_fifo = device_info->tx_fifo; 4559f2d3eaeSJakob Unterwurzacher strcpy(bittiming->name, "ucan"); 4569f2d3eaeSJakob Unterwurzacher bittiming->tseg1_min = device_info->tseg1_min; 4579f2d3eaeSJakob Unterwurzacher bittiming->tseg1_max = device_info->tseg1_max; 4589f2d3eaeSJakob Unterwurzacher bittiming->tseg2_min = device_info->tseg2_min; 4599f2d3eaeSJakob Unterwurzacher bittiming->tseg2_max = device_info->tseg2_max; 4609f2d3eaeSJakob Unterwurzacher bittiming->sjw_max = device_info->sjw_max; 4619f2d3eaeSJakob Unterwurzacher bittiming->brp_min = le32_to_cpu(device_info->brp_min); 4629f2d3eaeSJakob Unterwurzacher bittiming->brp_max = le32_to_cpu(device_info->brp_max); 4639f2d3eaeSJakob Unterwurzacher bittiming->brp_inc = le16_to_cpu(device_info->brp_inc); 4649f2d3eaeSJakob Unterwurzacher 4659f2d3eaeSJakob Unterwurzacher ctrlmodes = le16_to_cpu(device_info->ctrlmodes); 4669f2d3eaeSJakob Unterwurzacher 4679f2d3eaeSJakob Unterwurzacher up->can.ctrlmode_supported = 0; 4689f2d3eaeSJakob Unterwurzacher 4699f2d3eaeSJakob Unterwurzacher if (ctrlmodes & UCAN_MODE_LOOPBACK) 4709f2d3eaeSJakob Unterwurzacher up->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK; 4719f2d3eaeSJakob Unterwurzacher if (ctrlmodes & UCAN_MODE_SILENT) 4729f2d3eaeSJakob Unterwurzacher up->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; 4739f2d3eaeSJakob Unterwurzacher if (ctrlmodes & UCAN_MODE_3_SAMPLES) 4749f2d3eaeSJakob Unterwurzacher up->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; 4759f2d3eaeSJakob Unterwurzacher if (ctrlmodes & UCAN_MODE_ONE_SHOT) 4769f2d3eaeSJakob Unterwurzacher up->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; 4779f2d3eaeSJakob Unterwurzacher if (ctrlmodes & UCAN_MODE_BERR_REPORT) 4789f2d3eaeSJakob Unterwurzacher up->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING; 4799f2d3eaeSJakob Unterwurzacher } 4809f2d3eaeSJakob Unterwurzacher 4819f2d3eaeSJakob Unterwurzacher /* Handle a CAN error frame that we have received from the device. 4829f2d3eaeSJakob Unterwurzacher * Returns true if the can state has changed. 4839f2d3eaeSJakob Unterwurzacher */ 4849f2d3eaeSJakob Unterwurzacher static bool ucan_handle_error_frame(struct ucan_priv *up, 4859f2d3eaeSJakob Unterwurzacher struct ucan_message_in *m, 4869f2d3eaeSJakob Unterwurzacher canid_t canid) 4879f2d3eaeSJakob Unterwurzacher { 4889f2d3eaeSJakob Unterwurzacher enum can_state new_state = up->can.state; 4899f2d3eaeSJakob Unterwurzacher struct net_device_stats *net_stats = &up->netdev->stats; 4909f2d3eaeSJakob Unterwurzacher struct can_device_stats *can_stats = &up->can.can_stats; 4919f2d3eaeSJakob Unterwurzacher 4929f2d3eaeSJakob Unterwurzacher if (canid & CAN_ERR_LOSTARB) 4939f2d3eaeSJakob Unterwurzacher can_stats->arbitration_lost++; 4949f2d3eaeSJakob Unterwurzacher 4959f2d3eaeSJakob Unterwurzacher if (canid & CAN_ERR_BUSERROR) 4969f2d3eaeSJakob Unterwurzacher can_stats->bus_error++; 4979f2d3eaeSJakob Unterwurzacher 4989f2d3eaeSJakob Unterwurzacher if (canid & CAN_ERR_ACK) 4999f2d3eaeSJakob Unterwurzacher net_stats->tx_errors++; 5009f2d3eaeSJakob Unterwurzacher 5019f2d3eaeSJakob Unterwurzacher if (canid & CAN_ERR_BUSOFF) 5029f2d3eaeSJakob Unterwurzacher new_state = CAN_STATE_BUS_OFF; 5039f2d3eaeSJakob Unterwurzacher 5049f2d3eaeSJakob Unterwurzacher /* controller problems, details in data[1] */ 5059f2d3eaeSJakob Unterwurzacher if (canid & CAN_ERR_CRTL) { 5069f2d3eaeSJakob Unterwurzacher u8 d1 = m->msg.can_msg.data[1]; 5079f2d3eaeSJakob Unterwurzacher 5089f2d3eaeSJakob Unterwurzacher if (d1 & CAN_ERR_CRTL_RX_OVERFLOW) 5099f2d3eaeSJakob Unterwurzacher net_stats->rx_over_errors++; 5109f2d3eaeSJakob Unterwurzacher 5119f2d3eaeSJakob Unterwurzacher /* controller state bits: if multiple are set the worst wins */ 5129f2d3eaeSJakob Unterwurzacher if (d1 & CAN_ERR_CRTL_ACTIVE) 5139f2d3eaeSJakob Unterwurzacher new_state = CAN_STATE_ERROR_ACTIVE; 5149f2d3eaeSJakob Unterwurzacher 5159f2d3eaeSJakob Unterwurzacher if (d1 & (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING)) 5169f2d3eaeSJakob Unterwurzacher new_state = CAN_STATE_ERROR_WARNING; 5179f2d3eaeSJakob Unterwurzacher 5189f2d3eaeSJakob Unterwurzacher if (d1 & (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE)) 5199f2d3eaeSJakob Unterwurzacher new_state = CAN_STATE_ERROR_PASSIVE; 5209f2d3eaeSJakob Unterwurzacher } 5219f2d3eaeSJakob Unterwurzacher 5229f2d3eaeSJakob Unterwurzacher /* protocol error, details in data[2] */ 5239f2d3eaeSJakob Unterwurzacher if (canid & CAN_ERR_PROT) { 5249f2d3eaeSJakob Unterwurzacher u8 d2 = m->msg.can_msg.data[2]; 5259f2d3eaeSJakob Unterwurzacher 5269f2d3eaeSJakob Unterwurzacher if (d2 & CAN_ERR_PROT_TX) 5279f2d3eaeSJakob Unterwurzacher net_stats->tx_errors++; 5289f2d3eaeSJakob Unterwurzacher else 5299f2d3eaeSJakob Unterwurzacher net_stats->rx_errors++; 5309f2d3eaeSJakob Unterwurzacher } 5319f2d3eaeSJakob Unterwurzacher 5329f2d3eaeSJakob Unterwurzacher /* no state change - we are done */ 5339f2d3eaeSJakob Unterwurzacher if (up->can.state == new_state) 5349f2d3eaeSJakob Unterwurzacher return false; 5359f2d3eaeSJakob Unterwurzacher 5369f2d3eaeSJakob Unterwurzacher /* we switched into a better state */ 5379f2d3eaeSJakob Unterwurzacher if (up->can.state > new_state) { 5389f2d3eaeSJakob Unterwurzacher up->can.state = new_state; 5399f2d3eaeSJakob Unterwurzacher return true; 5409f2d3eaeSJakob Unterwurzacher } 5419f2d3eaeSJakob Unterwurzacher 5429f2d3eaeSJakob Unterwurzacher /* we switched into a worse state */ 5439f2d3eaeSJakob Unterwurzacher up->can.state = new_state; 5449f2d3eaeSJakob Unterwurzacher switch (new_state) { 5459f2d3eaeSJakob Unterwurzacher case CAN_STATE_BUS_OFF: 5469f2d3eaeSJakob Unterwurzacher can_stats->bus_off++; 5479f2d3eaeSJakob Unterwurzacher can_bus_off(up->netdev); 5489f2d3eaeSJakob Unterwurzacher break; 5499f2d3eaeSJakob Unterwurzacher case CAN_STATE_ERROR_PASSIVE: 5509f2d3eaeSJakob Unterwurzacher can_stats->error_passive++; 5519f2d3eaeSJakob Unterwurzacher break; 5529f2d3eaeSJakob Unterwurzacher case CAN_STATE_ERROR_WARNING: 5539f2d3eaeSJakob Unterwurzacher can_stats->error_warning++; 5549f2d3eaeSJakob Unterwurzacher break; 5559f2d3eaeSJakob Unterwurzacher default: 5569f2d3eaeSJakob Unterwurzacher break; 5579f2d3eaeSJakob Unterwurzacher } 5589f2d3eaeSJakob Unterwurzacher return true; 5599f2d3eaeSJakob Unterwurzacher } 5609f2d3eaeSJakob Unterwurzacher 5619f2d3eaeSJakob Unterwurzacher /* Callback on reception of a can frame via the IN endpoint 5629f2d3eaeSJakob Unterwurzacher * 5639f2d3eaeSJakob Unterwurzacher * This function allocates an skb and transferres it to the Linux 5649f2d3eaeSJakob Unterwurzacher * network stack 5659f2d3eaeSJakob Unterwurzacher */ 5669f2d3eaeSJakob Unterwurzacher static void ucan_rx_can_msg(struct ucan_priv *up, struct ucan_message_in *m) 5679f2d3eaeSJakob Unterwurzacher { 5689f2d3eaeSJakob Unterwurzacher int len; 5699f2d3eaeSJakob Unterwurzacher canid_t canid; 5709f2d3eaeSJakob Unterwurzacher struct can_frame *cf; 5719f2d3eaeSJakob Unterwurzacher struct sk_buff *skb; 5729f2d3eaeSJakob Unterwurzacher struct net_device_stats *stats = &up->netdev->stats; 5739f2d3eaeSJakob Unterwurzacher 5749f2d3eaeSJakob Unterwurzacher /* get the contents of the length field */ 5759f2d3eaeSJakob Unterwurzacher len = le16_to_cpu(m->len); 5769f2d3eaeSJakob Unterwurzacher 5779f2d3eaeSJakob Unterwurzacher /* check sanity */ 5789f2d3eaeSJakob Unterwurzacher if (len < UCAN_IN_HDR_SIZE + sizeof(m->msg.can_msg.id)) { 5799f2d3eaeSJakob Unterwurzacher netdev_warn(up->netdev, "invalid input message len: %d\n", len); 5809f2d3eaeSJakob Unterwurzacher return; 5819f2d3eaeSJakob Unterwurzacher } 5829f2d3eaeSJakob Unterwurzacher 5839f2d3eaeSJakob Unterwurzacher /* handle error frames */ 5849f2d3eaeSJakob Unterwurzacher canid = le32_to_cpu(m->msg.can_msg.id); 5859f2d3eaeSJakob Unterwurzacher if (canid & CAN_ERR_FLAG) { 5869f2d3eaeSJakob Unterwurzacher bool busstate_changed = ucan_handle_error_frame(up, m, canid); 5879f2d3eaeSJakob Unterwurzacher 5889f2d3eaeSJakob Unterwurzacher /* if berr-reporting is off only state changes get through */ 5899f2d3eaeSJakob Unterwurzacher if (!(up->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && 5909f2d3eaeSJakob Unterwurzacher !busstate_changed) 5919f2d3eaeSJakob Unterwurzacher return; 5929f2d3eaeSJakob Unterwurzacher } else { 5939f2d3eaeSJakob Unterwurzacher canid_t canid_mask; 5949f2d3eaeSJakob Unterwurzacher /* compute the mask for canid */ 5959f2d3eaeSJakob Unterwurzacher canid_mask = CAN_RTR_FLAG; 5969f2d3eaeSJakob Unterwurzacher if (canid & CAN_EFF_FLAG) 5979f2d3eaeSJakob Unterwurzacher canid_mask |= CAN_EFF_MASK | CAN_EFF_FLAG; 5989f2d3eaeSJakob Unterwurzacher else 5999f2d3eaeSJakob Unterwurzacher canid_mask |= CAN_SFF_MASK; 6009f2d3eaeSJakob Unterwurzacher 6019f2d3eaeSJakob Unterwurzacher if (canid & ~canid_mask) 6029f2d3eaeSJakob Unterwurzacher netdev_warn(up->netdev, 6039f2d3eaeSJakob Unterwurzacher "unexpected bits set (canid %x, mask %x)", 6049f2d3eaeSJakob Unterwurzacher canid, canid_mask); 6059f2d3eaeSJakob Unterwurzacher 6069f2d3eaeSJakob Unterwurzacher canid &= canid_mask; 6079f2d3eaeSJakob Unterwurzacher } 6089f2d3eaeSJakob Unterwurzacher 6099f2d3eaeSJakob Unterwurzacher /* allocate skb */ 6109f2d3eaeSJakob Unterwurzacher skb = alloc_can_skb(up->netdev, &cf); 6119f2d3eaeSJakob Unterwurzacher if (!skb) 6129f2d3eaeSJakob Unterwurzacher return; 6139f2d3eaeSJakob Unterwurzacher 6149f2d3eaeSJakob Unterwurzacher /* fill the can frame */ 6159f2d3eaeSJakob Unterwurzacher cf->can_id = canid; 6169f2d3eaeSJakob Unterwurzacher 6179f2d3eaeSJakob Unterwurzacher /* compute DLC taking RTR_FLAG into account */ 618c7b74967SOliver Hartkopp cf->len = ucan_can_cc_dlc2len(&m->msg.can_msg, len); 6199f2d3eaeSJakob Unterwurzacher 6209f2d3eaeSJakob Unterwurzacher /* copy the payload of non RTR frames */ 6219f2d3eaeSJakob Unterwurzacher if (!(cf->can_id & CAN_RTR_FLAG) || (cf->can_id & CAN_ERR_FLAG)) 622c7b74967SOliver Hartkopp memcpy(cf->data, m->msg.can_msg.data, cf->len); 6239f2d3eaeSJakob Unterwurzacher 6249f2d3eaeSJakob Unterwurzacher /* don't count error frames as real packets */ 625676068dbSVincent Mailhol if (!(cf->can_id & CAN_ERR_FLAG)) { 6269f2d3eaeSJakob Unterwurzacher stats->rx_packets++; 6278e674ca7SVincent Mailhol if (!(cf->can_id & CAN_RTR_FLAG)) 628c7b74967SOliver Hartkopp stats->rx_bytes += cf->len; 629676068dbSVincent Mailhol } 6309f2d3eaeSJakob Unterwurzacher 6319f2d3eaeSJakob Unterwurzacher /* pass it to Linux */ 6329f2d3eaeSJakob Unterwurzacher netif_rx(skb); 6339f2d3eaeSJakob Unterwurzacher } 6349f2d3eaeSJakob Unterwurzacher 6359f2d3eaeSJakob Unterwurzacher /* callback indicating completed transmission */ 6369f2d3eaeSJakob Unterwurzacher static void ucan_tx_complete_msg(struct ucan_priv *up, 6379f2d3eaeSJakob Unterwurzacher struct ucan_message_in *m) 6389f2d3eaeSJakob Unterwurzacher { 6399f2d3eaeSJakob Unterwurzacher unsigned long flags; 6409f2d3eaeSJakob Unterwurzacher u16 count, i; 641cc4b08c3SVincent Mailhol u8 echo_index; 6429f2d3eaeSJakob Unterwurzacher u16 len = le16_to_cpu(m->len); 6439f2d3eaeSJakob Unterwurzacher 6449f2d3eaeSJakob Unterwurzacher struct ucan_urb_context *context; 6459f2d3eaeSJakob Unterwurzacher 6469f2d3eaeSJakob Unterwurzacher if (len < UCAN_IN_HDR_SIZE || (len % 2 != 0)) { 6479f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, "invalid tx complete length\n"); 6489f2d3eaeSJakob Unterwurzacher return; 6499f2d3eaeSJakob Unterwurzacher } 6509f2d3eaeSJakob Unterwurzacher 6519f2d3eaeSJakob Unterwurzacher count = (len - UCAN_IN_HDR_SIZE) / 2; 6529f2d3eaeSJakob Unterwurzacher for (i = 0; i < count; i++) { 6539f2d3eaeSJakob Unterwurzacher /* we did not submit such echo ids */ 6549f2d3eaeSJakob Unterwurzacher echo_index = m->msg.can_tx_complete_msg[i].echo_index; 6559f2d3eaeSJakob Unterwurzacher if (echo_index >= up->device_info.tx_fifo) { 6569f2d3eaeSJakob Unterwurzacher up->netdev->stats.tx_errors++; 6579f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 6589f2d3eaeSJakob Unterwurzacher "invalid echo_index %d received\n", 6599f2d3eaeSJakob Unterwurzacher echo_index); 6609f2d3eaeSJakob Unterwurzacher continue; 6619f2d3eaeSJakob Unterwurzacher } 6629f2d3eaeSJakob Unterwurzacher 6639f2d3eaeSJakob Unterwurzacher /* gather information from the context */ 6649f2d3eaeSJakob Unterwurzacher context = &up->context_array[echo_index]; 6659f2d3eaeSJakob Unterwurzacher 6669f2d3eaeSJakob Unterwurzacher /* Release context and restart queue if necessary. 6679f2d3eaeSJakob Unterwurzacher * Also check if the context was allocated 6689f2d3eaeSJakob Unterwurzacher */ 6699f2d3eaeSJakob Unterwurzacher if (!ucan_release_context(up, context)) 6709f2d3eaeSJakob Unterwurzacher continue; 6719f2d3eaeSJakob Unterwurzacher 6729f2d3eaeSJakob Unterwurzacher spin_lock_irqsave(&up->echo_skb_lock, flags); 6739f2d3eaeSJakob Unterwurzacher if (m->msg.can_tx_complete_msg[i].flags & 6749f2d3eaeSJakob Unterwurzacher UCAN_TX_COMPLETE_SUCCESS) { 6759f2d3eaeSJakob Unterwurzacher /* update statistics */ 6769f2d3eaeSJakob Unterwurzacher up->netdev->stats.tx_packets++; 677cc4b08c3SVincent Mailhol up->netdev->stats.tx_bytes += 6789420e1d4SMarc Kleine-Budde can_get_echo_skb(up->netdev, echo_index, NULL); 6799f2d3eaeSJakob Unterwurzacher } else { 6809f2d3eaeSJakob Unterwurzacher up->netdev->stats.tx_dropped++; 681f318482aSMarc Kleine-Budde can_free_echo_skb(up->netdev, echo_index, NULL); 6829f2d3eaeSJakob Unterwurzacher } 6839f2d3eaeSJakob Unterwurzacher spin_unlock_irqrestore(&up->echo_skb_lock, flags); 6849f2d3eaeSJakob Unterwurzacher } 6859f2d3eaeSJakob Unterwurzacher } 6869f2d3eaeSJakob Unterwurzacher 6879f2d3eaeSJakob Unterwurzacher /* callback on reception of a USB message */ 6889f2d3eaeSJakob Unterwurzacher static void ucan_read_bulk_callback(struct urb *urb) 6899f2d3eaeSJakob Unterwurzacher { 6909f2d3eaeSJakob Unterwurzacher int ret; 6919f2d3eaeSJakob Unterwurzacher int pos; 6929f2d3eaeSJakob Unterwurzacher struct ucan_priv *up = urb->context; 6939f2d3eaeSJakob Unterwurzacher struct net_device *netdev = up->netdev; 6949f2d3eaeSJakob Unterwurzacher struct ucan_message_in *m; 6959f2d3eaeSJakob Unterwurzacher 6969f2d3eaeSJakob Unterwurzacher /* the device is not up and the driver should not receive any 6979f2d3eaeSJakob Unterwurzacher * data on the bulk in pipe 6989f2d3eaeSJakob Unterwurzacher */ 6999f2d3eaeSJakob Unterwurzacher if (WARN_ON(!up->context_array)) { 7009f2d3eaeSJakob Unterwurzacher usb_free_coherent(up->udev, 7019f2d3eaeSJakob Unterwurzacher up->in_ep_size, 7029f2d3eaeSJakob Unterwurzacher urb->transfer_buffer, 7039f2d3eaeSJakob Unterwurzacher urb->transfer_dma); 7049f2d3eaeSJakob Unterwurzacher return; 7059f2d3eaeSJakob Unterwurzacher } 7069f2d3eaeSJakob Unterwurzacher 7079f2d3eaeSJakob Unterwurzacher /* check URB status */ 7089f2d3eaeSJakob Unterwurzacher switch (urb->status) { 7099f2d3eaeSJakob Unterwurzacher case 0: 7109f2d3eaeSJakob Unterwurzacher break; 7119f2d3eaeSJakob Unterwurzacher case -ENOENT: 7129f2d3eaeSJakob Unterwurzacher case -EPIPE: 7139f2d3eaeSJakob Unterwurzacher case -EPROTO: 7149f2d3eaeSJakob Unterwurzacher case -ESHUTDOWN: 7159f2d3eaeSJakob Unterwurzacher case -ETIME: 7169f2d3eaeSJakob Unterwurzacher /* urb is not resubmitted -> free dma data */ 7179f2d3eaeSJakob Unterwurzacher usb_free_coherent(up->udev, 7189f2d3eaeSJakob Unterwurzacher up->in_ep_size, 7199f2d3eaeSJakob Unterwurzacher urb->transfer_buffer, 7209f2d3eaeSJakob Unterwurzacher urb->transfer_dma); 7213b17d417SColin Ian King netdev_dbg(up->netdev, "not resubmitting urb; status: %d\n", 7229f2d3eaeSJakob Unterwurzacher urb->status); 7239f2d3eaeSJakob Unterwurzacher return; 7249f2d3eaeSJakob Unterwurzacher default: 7259f2d3eaeSJakob Unterwurzacher goto resubmit; 7269f2d3eaeSJakob Unterwurzacher } 7279f2d3eaeSJakob Unterwurzacher 7289f2d3eaeSJakob Unterwurzacher /* sanity check */ 7299f2d3eaeSJakob Unterwurzacher if (!netif_device_present(netdev)) 7309f2d3eaeSJakob Unterwurzacher return; 7319f2d3eaeSJakob Unterwurzacher 7329f2d3eaeSJakob Unterwurzacher /* iterate over input */ 7339f2d3eaeSJakob Unterwurzacher pos = 0; 7349f2d3eaeSJakob Unterwurzacher while (pos < urb->actual_length) { 7359f2d3eaeSJakob Unterwurzacher int len; 7369f2d3eaeSJakob Unterwurzacher 7379f2d3eaeSJakob Unterwurzacher /* check sanity (length of header) */ 7389f2d3eaeSJakob Unterwurzacher if ((urb->actual_length - pos) < UCAN_IN_HDR_SIZE) { 7399f2d3eaeSJakob Unterwurzacher netdev_warn(up->netdev, 7409f2d3eaeSJakob Unterwurzacher "invalid message (short; no hdr; l:%d)\n", 7419f2d3eaeSJakob Unterwurzacher urb->actual_length); 7429f2d3eaeSJakob Unterwurzacher goto resubmit; 7439f2d3eaeSJakob Unterwurzacher } 7449f2d3eaeSJakob Unterwurzacher 7459f2d3eaeSJakob Unterwurzacher /* setup the message address */ 7469f2d3eaeSJakob Unterwurzacher m = (struct ucan_message_in *) 7479f2d3eaeSJakob Unterwurzacher ((u8 *)urb->transfer_buffer + pos); 7489f2d3eaeSJakob Unterwurzacher len = le16_to_cpu(m->len); 7499f2d3eaeSJakob Unterwurzacher 7509f2d3eaeSJakob Unterwurzacher /* check sanity (length of content) */ 7519f2d3eaeSJakob Unterwurzacher if (urb->actual_length - pos < len) { 7529f2d3eaeSJakob Unterwurzacher netdev_warn(up->netdev, 7539f2d3eaeSJakob Unterwurzacher "invalid message (short; no data; l:%d)\n", 7549f2d3eaeSJakob Unterwurzacher urb->actual_length); 7559f2d3eaeSJakob Unterwurzacher print_hex_dump(KERN_WARNING, 7569f2d3eaeSJakob Unterwurzacher "raw data: ", 7579f2d3eaeSJakob Unterwurzacher DUMP_PREFIX_ADDRESS, 7589f2d3eaeSJakob Unterwurzacher 16, 7599f2d3eaeSJakob Unterwurzacher 1, 7609f2d3eaeSJakob Unterwurzacher urb->transfer_buffer, 7619f2d3eaeSJakob Unterwurzacher urb->actual_length, 7629f2d3eaeSJakob Unterwurzacher true); 7639f2d3eaeSJakob Unterwurzacher 7649f2d3eaeSJakob Unterwurzacher goto resubmit; 7659f2d3eaeSJakob Unterwurzacher } 7669f2d3eaeSJakob Unterwurzacher 7679f2d3eaeSJakob Unterwurzacher switch (m->type) { 7689f2d3eaeSJakob Unterwurzacher case UCAN_IN_RX: 7699f2d3eaeSJakob Unterwurzacher ucan_rx_can_msg(up, m); 7709f2d3eaeSJakob Unterwurzacher break; 7719f2d3eaeSJakob Unterwurzacher case UCAN_IN_TX_COMPLETE: 7729f2d3eaeSJakob Unterwurzacher ucan_tx_complete_msg(up, m); 7739f2d3eaeSJakob Unterwurzacher break; 7749f2d3eaeSJakob Unterwurzacher default: 7759f2d3eaeSJakob Unterwurzacher netdev_warn(up->netdev, 7769f2d3eaeSJakob Unterwurzacher "invalid message (type; t:%d)\n", 7779f2d3eaeSJakob Unterwurzacher m->type); 7789f2d3eaeSJakob Unterwurzacher break; 7799f2d3eaeSJakob Unterwurzacher } 7809f2d3eaeSJakob Unterwurzacher 7819f2d3eaeSJakob Unterwurzacher /* proceed to next message */ 7829f2d3eaeSJakob Unterwurzacher pos += len; 7839f2d3eaeSJakob Unterwurzacher /* align to 4 byte boundary */ 7849f2d3eaeSJakob Unterwurzacher pos = round_up(pos, 4); 7859f2d3eaeSJakob Unterwurzacher } 7869f2d3eaeSJakob Unterwurzacher 7879f2d3eaeSJakob Unterwurzacher resubmit: 7889f2d3eaeSJakob Unterwurzacher /* resubmit urb when done */ 7899f2d3eaeSJakob Unterwurzacher usb_fill_bulk_urb(urb, up->udev, 7909f2d3eaeSJakob Unterwurzacher usb_rcvbulkpipe(up->udev, 7919f2d3eaeSJakob Unterwurzacher up->in_ep_addr), 7929f2d3eaeSJakob Unterwurzacher urb->transfer_buffer, 7939f2d3eaeSJakob Unterwurzacher up->in_ep_size, 7949f2d3eaeSJakob Unterwurzacher ucan_read_bulk_callback, 7959f2d3eaeSJakob Unterwurzacher up); 7969f2d3eaeSJakob Unterwurzacher 7979f2d3eaeSJakob Unterwurzacher usb_anchor_urb(urb, &up->rx_urbs); 798870db5d1SJohan Hovold ret = usb_submit_urb(urb, GFP_ATOMIC); 7999f2d3eaeSJakob Unterwurzacher 8009f2d3eaeSJakob Unterwurzacher if (ret < 0) { 8019f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 8029f2d3eaeSJakob Unterwurzacher "failed resubmitting read bulk urb: %d\n", 8039f2d3eaeSJakob Unterwurzacher ret); 8049f2d3eaeSJakob Unterwurzacher 8059f2d3eaeSJakob Unterwurzacher usb_unanchor_urb(urb); 8069f2d3eaeSJakob Unterwurzacher usb_free_coherent(up->udev, 8079f2d3eaeSJakob Unterwurzacher up->in_ep_size, 8089f2d3eaeSJakob Unterwurzacher urb->transfer_buffer, 8099f2d3eaeSJakob Unterwurzacher urb->transfer_dma); 8109f2d3eaeSJakob Unterwurzacher 8119f2d3eaeSJakob Unterwurzacher if (ret == -ENODEV) 8129f2d3eaeSJakob Unterwurzacher netif_device_detach(netdev); 8139f2d3eaeSJakob Unterwurzacher } 8149f2d3eaeSJakob Unterwurzacher } 8159f2d3eaeSJakob Unterwurzacher 8169f2d3eaeSJakob Unterwurzacher /* callback after transmission of a USB message */ 8179f2d3eaeSJakob Unterwurzacher static void ucan_write_bulk_callback(struct urb *urb) 8189f2d3eaeSJakob Unterwurzacher { 8199f2d3eaeSJakob Unterwurzacher unsigned long flags; 8209f2d3eaeSJakob Unterwurzacher struct ucan_priv *up; 8219f2d3eaeSJakob Unterwurzacher struct ucan_urb_context *context = urb->context; 8229f2d3eaeSJakob Unterwurzacher 8239f2d3eaeSJakob Unterwurzacher /* get the urb context */ 8249f2d3eaeSJakob Unterwurzacher if (WARN_ON_ONCE(!context)) 8259f2d3eaeSJakob Unterwurzacher return; 8269f2d3eaeSJakob Unterwurzacher 8279f2d3eaeSJakob Unterwurzacher /* free up our allocated buffer */ 8289f2d3eaeSJakob Unterwurzacher usb_free_coherent(urb->dev, 8299f2d3eaeSJakob Unterwurzacher sizeof(struct ucan_message_out), 8309f2d3eaeSJakob Unterwurzacher urb->transfer_buffer, 8319f2d3eaeSJakob Unterwurzacher urb->transfer_dma); 8329f2d3eaeSJakob Unterwurzacher 8339f2d3eaeSJakob Unterwurzacher up = context->up; 8349f2d3eaeSJakob Unterwurzacher if (WARN_ON_ONCE(!up)) 8359f2d3eaeSJakob Unterwurzacher return; 8369f2d3eaeSJakob Unterwurzacher 8379f2d3eaeSJakob Unterwurzacher /* sanity check */ 8389f2d3eaeSJakob Unterwurzacher if (!netif_device_present(up->netdev)) 8399f2d3eaeSJakob Unterwurzacher return; 8409f2d3eaeSJakob Unterwurzacher 8419f2d3eaeSJakob Unterwurzacher /* transmission failed (USB - the device will not send a TX complete) */ 8429f2d3eaeSJakob Unterwurzacher if (urb->status) { 8439f2d3eaeSJakob Unterwurzacher netdev_warn(up->netdev, 8449f2d3eaeSJakob Unterwurzacher "failed to transmit USB message to device: %d\n", 8459f2d3eaeSJakob Unterwurzacher urb->status); 8469f2d3eaeSJakob Unterwurzacher 8479f2d3eaeSJakob Unterwurzacher /* update counters an cleanup */ 8489f2d3eaeSJakob Unterwurzacher spin_lock_irqsave(&up->echo_skb_lock, flags); 849f318482aSMarc Kleine-Budde can_free_echo_skb(up->netdev, context - up->context_array, NULL); 8509f2d3eaeSJakob Unterwurzacher spin_unlock_irqrestore(&up->echo_skb_lock, flags); 8519f2d3eaeSJakob Unterwurzacher 8529f2d3eaeSJakob Unterwurzacher up->netdev->stats.tx_dropped++; 8539f2d3eaeSJakob Unterwurzacher 8549f2d3eaeSJakob Unterwurzacher /* release context and restart the queue if necessary */ 8559f2d3eaeSJakob Unterwurzacher if (!ucan_release_context(up, context)) 8569f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 8579f2d3eaeSJakob Unterwurzacher "urb failed, failed to release context\n"); 8589f2d3eaeSJakob Unterwurzacher } 8599f2d3eaeSJakob Unterwurzacher } 8609f2d3eaeSJakob Unterwurzacher 8619f2d3eaeSJakob Unterwurzacher static void ucan_cleanup_rx_urbs(struct ucan_priv *up, struct urb **urbs) 8629f2d3eaeSJakob Unterwurzacher { 8639f2d3eaeSJakob Unterwurzacher int i; 8649f2d3eaeSJakob Unterwurzacher 8659f2d3eaeSJakob Unterwurzacher for (i = 0; i < UCAN_MAX_RX_URBS; i++) { 8669f2d3eaeSJakob Unterwurzacher if (urbs[i]) { 8679f2d3eaeSJakob Unterwurzacher usb_unanchor_urb(urbs[i]); 8689f2d3eaeSJakob Unterwurzacher usb_free_coherent(up->udev, 8699f2d3eaeSJakob Unterwurzacher up->in_ep_size, 8709f2d3eaeSJakob Unterwurzacher urbs[i]->transfer_buffer, 8719f2d3eaeSJakob Unterwurzacher urbs[i]->transfer_dma); 8729f2d3eaeSJakob Unterwurzacher usb_free_urb(urbs[i]); 8739f2d3eaeSJakob Unterwurzacher } 8749f2d3eaeSJakob Unterwurzacher } 8759f2d3eaeSJakob Unterwurzacher 8769f2d3eaeSJakob Unterwurzacher memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS); 8779f2d3eaeSJakob Unterwurzacher } 8789f2d3eaeSJakob Unterwurzacher 8799f2d3eaeSJakob Unterwurzacher static int ucan_prepare_and_anchor_rx_urbs(struct ucan_priv *up, 8809f2d3eaeSJakob Unterwurzacher struct urb **urbs) 8819f2d3eaeSJakob Unterwurzacher { 8829f2d3eaeSJakob Unterwurzacher int i; 8839f2d3eaeSJakob Unterwurzacher 8849f2d3eaeSJakob Unterwurzacher memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS); 8859f2d3eaeSJakob Unterwurzacher 8869f2d3eaeSJakob Unterwurzacher for (i = 0; i < UCAN_MAX_RX_URBS; i++) { 8879f2d3eaeSJakob Unterwurzacher void *buf; 8889f2d3eaeSJakob Unterwurzacher 8899f2d3eaeSJakob Unterwurzacher urbs[i] = usb_alloc_urb(0, GFP_KERNEL); 8909f2d3eaeSJakob Unterwurzacher if (!urbs[i]) 8919f2d3eaeSJakob Unterwurzacher goto err; 8929f2d3eaeSJakob Unterwurzacher 8939f2d3eaeSJakob Unterwurzacher buf = usb_alloc_coherent(up->udev, 8949f2d3eaeSJakob Unterwurzacher up->in_ep_size, 8959f2d3eaeSJakob Unterwurzacher GFP_KERNEL, &urbs[i]->transfer_dma); 8969f2d3eaeSJakob Unterwurzacher if (!buf) { 8979f2d3eaeSJakob Unterwurzacher /* cleanup this urb */ 8989f2d3eaeSJakob Unterwurzacher usb_free_urb(urbs[i]); 8999f2d3eaeSJakob Unterwurzacher urbs[i] = NULL; 9009f2d3eaeSJakob Unterwurzacher goto err; 9019f2d3eaeSJakob Unterwurzacher } 9029f2d3eaeSJakob Unterwurzacher 9039f2d3eaeSJakob Unterwurzacher usb_fill_bulk_urb(urbs[i], up->udev, 9049f2d3eaeSJakob Unterwurzacher usb_rcvbulkpipe(up->udev, 9059f2d3eaeSJakob Unterwurzacher up->in_ep_addr), 9069f2d3eaeSJakob Unterwurzacher buf, 9079f2d3eaeSJakob Unterwurzacher up->in_ep_size, 9089f2d3eaeSJakob Unterwurzacher ucan_read_bulk_callback, 9099f2d3eaeSJakob Unterwurzacher up); 9109f2d3eaeSJakob Unterwurzacher 9119f2d3eaeSJakob Unterwurzacher urbs[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 9129f2d3eaeSJakob Unterwurzacher 9139f2d3eaeSJakob Unterwurzacher usb_anchor_urb(urbs[i], &up->rx_urbs); 9149f2d3eaeSJakob Unterwurzacher } 9159f2d3eaeSJakob Unterwurzacher return 0; 9169f2d3eaeSJakob Unterwurzacher 9179f2d3eaeSJakob Unterwurzacher err: 9189f2d3eaeSJakob Unterwurzacher /* cleanup other unsubmitted urbs */ 9199f2d3eaeSJakob Unterwurzacher ucan_cleanup_rx_urbs(up, urbs); 9209f2d3eaeSJakob Unterwurzacher return -ENOMEM; 9219f2d3eaeSJakob Unterwurzacher } 9229f2d3eaeSJakob Unterwurzacher 9239f2d3eaeSJakob Unterwurzacher /* Submits rx urbs with the semantic: Either submit all, or cleanup 9249f2d3eaeSJakob Unterwurzacher * everything. I case of errors submitted urbs are killed and all urbs in 9259f2d3eaeSJakob Unterwurzacher * the array are freed. I case of no errors every entry in the urb 9269f2d3eaeSJakob Unterwurzacher * array is set to NULL. 9279f2d3eaeSJakob Unterwurzacher */ 9289f2d3eaeSJakob Unterwurzacher static int ucan_submit_rx_urbs(struct ucan_priv *up, struct urb **urbs) 9299f2d3eaeSJakob Unterwurzacher { 9309f2d3eaeSJakob Unterwurzacher int i, ret; 9319f2d3eaeSJakob Unterwurzacher 9329f2d3eaeSJakob Unterwurzacher /* Iterate over all urbs to submit. On success remove the urb 9339f2d3eaeSJakob Unterwurzacher * from the list. 9349f2d3eaeSJakob Unterwurzacher */ 9359f2d3eaeSJakob Unterwurzacher for (i = 0; i < UCAN_MAX_RX_URBS; i++) { 9369f2d3eaeSJakob Unterwurzacher ret = usb_submit_urb(urbs[i], GFP_KERNEL); 9379f2d3eaeSJakob Unterwurzacher if (ret) { 9389f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 9399f2d3eaeSJakob Unterwurzacher "could not submit urb; code: %d\n", 9409f2d3eaeSJakob Unterwurzacher ret); 9419f2d3eaeSJakob Unterwurzacher goto err; 9429f2d3eaeSJakob Unterwurzacher } 9439f2d3eaeSJakob Unterwurzacher 9449f2d3eaeSJakob Unterwurzacher /* Anchor URB and drop reference, USB core will take 9459f2d3eaeSJakob Unterwurzacher * care of freeing it 9469f2d3eaeSJakob Unterwurzacher */ 9479f2d3eaeSJakob Unterwurzacher usb_free_urb(urbs[i]); 9489f2d3eaeSJakob Unterwurzacher urbs[i] = NULL; 9499f2d3eaeSJakob Unterwurzacher } 9509f2d3eaeSJakob Unterwurzacher return 0; 9519f2d3eaeSJakob Unterwurzacher 9529f2d3eaeSJakob Unterwurzacher err: 9539f2d3eaeSJakob Unterwurzacher /* Cleanup unsubmitted urbs */ 9549f2d3eaeSJakob Unterwurzacher ucan_cleanup_rx_urbs(up, urbs); 9559f2d3eaeSJakob Unterwurzacher 9569f2d3eaeSJakob Unterwurzacher /* Kill urbs that are already submitted */ 9579f2d3eaeSJakob Unterwurzacher usb_kill_anchored_urbs(&up->rx_urbs); 9589f2d3eaeSJakob Unterwurzacher 9599f2d3eaeSJakob Unterwurzacher return ret; 9609f2d3eaeSJakob Unterwurzacher } 9619f2d3eaeSJakob Unterwurzacher 9629f2d3eaeSJakob Unterwurzacher /* Open the network device */ 9639f2d3eaeSJakob Unterwurzacher static int ucan_open(struct net_device *netdev) 9649f2d3eaeSJakob Unterwurzacher { 9659f2d3eaeSJakob Unterwurzacher int ret, ret_cleanup; 9669f2d3eaeSJakob Unterwurzacher u16 ctrlmode; 9679f2d3eaeSJakob Unterwurzacher struct urb *urbs[UCAN_MAX_RX_URBS]; 9689f2d3eaeSJakob Unterwurzacher struct ucan_priv *up = netdev_priv(netdev); 9699f2d3eaeSJakob Unterwurzacher 9709f2d3eaeSJakob Unterwurzacher ret = ucan_alloc_context_array(up); 9719f2d3eaeSJakob Unterwurzacher if (ret) 9729f2d3eaeSJakob Unterwurzacher return ret; 9739f2d3eaeSJakob Unterwurzacher 9749f2d3eaeSJakob Unterwurzacher /* Allocate and prepare IN URBS - allocated and anchored 9759f2d3eaeSJakob Unterwurzacher * urbs are stored in urbs[] for clean 9769f2d3eaeSJakob Unterwurzacher */ 9779f2d3eaeSJakob Unterwurzacher ret = ucan_prepare_and_anchor_rx_urbs(up, urbs); 9789f2d3eaeSJakob Unterwurzacher if (ret) 9799f2d3eaeSJakob Unterwurzacher goto err_contexts; 9809f2d3eaeSJakob Unterwurzacher 9819f2d3eaeSJakob Unterwurzacher /* Check the control mode */ 9829f2d3eaeSJakob Unterwurzacher ctrlmode = 0; 9839f2d3eaeSJakob Unterwurzacher if (up->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) 9849f2d3eaeSJakob Unterwurzacher ctrlmode |= UCAN_MODE_LOOPBACK; 9859f2d3eaeSJakob Unterwurzacher if (up->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) 9869f2d3eaeSJakob Unterwurzacher ctrlmode |= UCAN_MODE_SILENT; 9879f2d3eaeSJakob Unterwurzacher if (up->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) 9889f2d3eaeSJakob Unterwurzacher ctrlmode |= UCAN_MODE_3_SAMPLES; 9899f2d3eaeSJakob Unterwurzacher if (up->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) 9909f2d3eaeSJakob Unterwurzacher ctrlmode |= UCAN_MODE_ONE_SHOT; 9919f2d3eaeSJakob Unterwurzacher 9929f2d3eaeSJakob Unterwurzacher /* Enable this in any case - filtering is down within the 9939f2d3eaeSJakob Unterwurzacher * receive path 9949f2d3eaeSJakob Unterwurzacher */ 9959f2d3eaeSJakob Unterwurzacher ctrlmode |= UCAN_MODE_BERR_REPORT; 9969f2d3eaeSJakob Unterwurzacher up->ctl_msg_buffer->cmd_start.mode = cpu_to_le16(ctrlmode); 9979f2d3eaeSJakob Unterwurzacher 9989f2d3eaeSJakob Unterwurzacher /* Driver is ready to receive data - start the USB device */ 9999f2d3eaeSJakob Unterwurzacher ret = ucan_ctrl_command_out(up, UCAN_COMMAND_START, 0, 2); 10009f2d3eaeSJakob Unterwurzacher if (ret < 0) { 10019f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 10029f2d3eaeSJakob Unterwurzacher "could not start device, code: %d\n", 10039f2d3eaeSJakob Unterwurzacher ret); 10049f2d3eaeSJakob Unterwurzacher goto err_reset; 10059f2d3eaeSJakob Unterwurzacher } 10069f2d3eaeSJakob Unterwurzacher 10079f2d3eaeSJakob Unterwurzacher /* Call CAN layer open */ 10089f2d3eaeSJakob Unterwurzacher ret = open_candev(netdev); 10099f2d3eaeSJakob Unterwurzacher if (ret) 10109f2d3eaeSJakob Unterwurzacher goto err_stop; 10119f2d3eaeSJakob Unterwurzacher 10129f2d3eaeSJakob Unterwurzacher /* Driver is ready to receive data. Submit RX URBS */ 10139f2d3eaeSJakob Unterwurzacher ret = ucan_submit_rx_urbs(up, urbs); 10149f2d3eaeSJakob Unterwurzacher if (ret) 10159f2d3eaeSJakob Unterwurzacher goto err_stop; 10169f2d3eaeSJakob Unterwurzacher 10179f2d3eaeSJakob Unterwurzacher up->can.state = CAN_STATE_ERROR_ACTIVE; 10189f2d3eaeSJakob Unterwurzacher 10199f2d3eaeSJakob Unterwurzacher /* Start the network queue */ 10209f2d3eaeSJakob Unterwurzacher netif_start_queue(netdev); 10219f2d3eaeSJakob Unterwurzacher 10229f2d3eaeSJakob Unterwurzacher return 0; 10239f2d3eaeSJakob Unterwurzacher 10249f2d3eaeSJakob Unterwurzacher err_stop: 10259f2d3eaeSJakob Unterwurzacher /* The device have started already stop it */ 10269f2d3eaeSJakob Unterwurzacher ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_STOP, 0, 0); 10279f2d3eaeSJakob Unterwurzacher if (ret_cleanup < 0) 10289f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 10299f2d3eaeSJakob Unterwurzacher "could not stop device, code: %d\n", 10309f2d3eaeSJakob Unterwurzacher ret_cleanup); 10319f2d3eaeSJakob Unterwurzacher 10329f2d3eaeSJakob Unterwurzacher err_reset: 10339f2d3eaeSJakob Unterwurzacher /* The device might have received data, reset it for 10349f2d3eaeSJakob Unterwurzacher * consistent state 10359f2d3eaeSJakob Unterwurzacher */ 10369f2d3eaeSJakob Unterwurzacher ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); 10379f2d3eaeSJakob Unterwurzacher if (ret_cleanup < 0) 10389f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 10399f2d3eaeSJakob Unterwurzacher "could not reset device, code: %d\n", 10409f2d3eaeSJakob Unterwurzacher ret_cleanup); 10419f2d3eaeSJakob Unterwurzacher 10429f2d3eaeSJakob Unterwurzacher /* clean up unsubmitted urbs */ 10439f2d3eaeSJakob Unterwurzacher ucan_cleanup_rx_urbs(up, urbs); 10449f2d3eaeSJakob Unterwurzacher 10459f2d3eaeSJakob Unterwurzacher err_contexts: 10469f2d3eaeSJakob Unterwurzacher ucan_release_context_array(up); 10479f2d3eaeSJakob Unterwurzacher return ret; 10489f2d3eaeSJakob Unterwurzacher } 10499f2d3eaeSJakob Unterwurzacher 10509f2d3eaeSJakob Unterwurzacher static struct urb *ucan_prepare_tx_urb(struct ucan_priv *up, 10519f2d3eaeSJakob Unterwurzacher struct ucan_urb_context *context, 10529f2d3eaeSJakob Unterwurzacher struct can_frame *cf, 10539f2d3eaeSJakob Unterwurzacher u8 echo_index) 10549f2d3eaeSJakob Unterwurzacher { 10559f2d3eaeSJakob Unterwurzacher int mlen; 10569f2d3eaeSJakob Unterwurzacher struct urb *urb; 10579f2d3eaeSJakob Unterwurzacher struct ucan_message_out *m; 10589f2d3eaeSJakob Unterwurzacher 10599f2d3eaeSJakob Unterwurzacher /* create a URB, and a buffer for it, and copy the data to the URB */ 10609f2d3eaeSJakob Unterwurzacher urb = usb_alloc_urb(0, GFP_ATOMIC); 10619f2d3eaeSJakob Unterwurzacher if (!urb) { 10629f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, "no memory left for URBs\n"); 10639f2d3eaeSJakob Unterwurzacher return NULL; 10649f2d3eaeSJakob Unterwurzacher } 10659f2d3eaeSJakob Unterwurzacher 10669f2d3eaeSJakob Unterwurzacher m = usb_alloc_coherent(up->udev, 10679f2d3eaeSJakob Unterwurzacher sizeof(struct ucan_message_out), 10689f2d3eaeSJakob Unterwurzacher GFP_ATOMIC, 10699f2d3eaeSJakob Unterwurzacher &urb->transfer_dma); 10709f2d3eaeSJakob Unterwurzacher if (!m) { 10719f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, "no memory left for USB buffer\n"); 10729f2d3eaeSJakob Unterwurzacher usb_free_urb(urb); 10739f2d3eaeSJakob Unterwurzacher return NULL; 10749f2d3eaeSJakob Unterwurzacher } 10759f2d3eaeSJakob Unterwurzacher 10769f2d3eaeSJakob Unterwurzacher /* build the USB message */ 10779f2d3eaeSJakob Unterwurzacher m->type = UCAN_OUT_TX; 10789f2d3eaeSJakob Unterwurzacher m->msg.can_msg.id = cpu_to_le32(cf->can_id); 10799f2d3eaeSJakob Unterwurzacher 10809f2d3eaeSJakob Unterwurzacher if (cf->can_id & CAN_RTR_FLAG) { 10819f2d3eaeSJakob Unterwurzacher mlen = UCAN_OUT_HDR_SIZE + 10829f2d3eaeSJakob Unterwurzacher offsetof(struct ucan_can_msg, dlc) + 10839f2d3eaeSJakob Unterwurzacher sizeof(m->msg.can_msg.dlc); 1084c7b74967SOliver Hartkopp m->msg.can_msg.dlc = cf->len; 10859f2d3eaeSJakob Unterwurzacher } else { 10869f2d3eaeSJakob Unterwurzacher mlen = UCAN_OUT_HDR_SIZE + 1087c7b74967SOliver Hartkopp sizeof(m->msg.can_msg.id) + cf->len; 1088c7b74967SOliver Hartkopp memcpy(m->msg.can_msg.data, cf->data, cf->len); 10899f2d3eaeSJakob Unterwurzacher } 10909f2d3eaeSJakob Unterwurzacher m->len = cpu_to_le16(mlen); 10919f2d3eaeSJakob Unterwurzacher 10929f2d3eaeSJakob Unterwurzacher m->subtype = echo_index; 10939f2d3eaeSJakob Unterwurzacher 10949f2d3eaeSJakob Unterwurzacher /* build the urb */ 10959f2d3eaeSJakob Unterwurzacher usb_fill_bulk_urb(urb, up->udev, 10969f2d3eaeSJakob Unterwurzacher usb_sndbulkpipe(up->udev, 10979f2d3eaeSJakob Unterwurzacher up->out_ep_addr), 10989f2d3eaeSJakob Unterwurzacher m, mlen, ucan_write_bulk_callback, context); 10999f2d3eaeSJakob Unterwurzacher urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 11009f2d3eaeSJakob Unterwurzacher 11019f2d3eaeSJakob Unterwurzacher return urb; 11029f2d3eaeSJakob Unterwurzacher } 11039f2d3eaeSJakob Unterwurzacher 11049f2d3eaeSJakob Unterwurzacher static void ucan_clean_up_tx_urb(struct ucan_priv *up, struct urb *urb) 11059f2d3eaeSJakob Unterwurzacher { 11069f2d3eaeSJakob Unterwurzacher usb_free_coherent(up->udev, sizeof(struct ucan_message_out), 11079f2d3eaeSJakob Unterwurzacher urb->transfer_buffer, urb->transfer_dma); 11089f2d3eaeSJakob Unterwurzacher usb_free_urb(urb); 11099f2d3eaeSJakob Unterwurzacher } 11109f2d3eaeSJakob Unterwurzacher 11119f2d3eaeSJakob Unterwurzacher /* callback when Linux needs to send a can frame */ 11129f2d3eaeSJakob Unterwurzacher static netdev_tx_t ucan_start_xmit(struct sk_buff *skb, 11139f2d3eaeSJakob Unterwurzacher struct net_device *netdev) 11149f2d3eaeSJakob Unterwurzacher { 11159f2d3eaeSJakob Unterwurzacher unsigned long flags; 11169f2d3eaeSJakob Unterwurzacher int ret; 11179f2d3eaeSJakob Unterwurzacher u8 echo_index; 11189f2d3eaeSJakob Unterwurzacher struct urb *urb; 11199f2d3eaeSJakob Unterwurzacher struct ucan_urb_context *context; 11209f2d3eaeSJakob Unterwurzacher struct ucan_priv *up = netdev_priv(netdev); 11219f2d3eaeSJakob Unterwurzacher struct can_frame *cf = (struct can_frame *)skb->data; 11229f2d3eaeSJakob Unterwurzacher 11239f2d3eaeSJakob Unterwurzacher /* check skb */ 11249f2d3eaeSJakob Unterwurzacher if (can_dropped_invalid_skb(netdev, skb)) 11259f2d3eaeSJakob Unterwurzacher return NETDEV_TX_OK; 11269f2d3eaeSJakob Unterwurzacher 11279f2d3eaeSJakob Unterwurzacher /* allocate a context and slow down tx path, if fifo state is low */ 11289f2d3eaeSJakob Unterwurzacher context = ucan_alloc_context(up); 11299f2d3eaeSJakob Unterwurzacher echo_index = context - up->context_array; 11309f2d3eaeSJakob Unterwurzacher 11319f2d3eaeSJakob Unterwurzacher if (WARN_ON_ONCE(!context)) 11329f2d3eaeSJakob Unterwurzacher return NETDEV_TX_BUSY; 11339f2d3eaeSJakob Unterwurzacher 11349f2d3eaeSJakob Unterwurzacher /* prepare urb for transmission */ 11359f2d3eaeSJakob Unterwurzacher urb = ucan_prepare_tx_urb(up, context, cf, echo_index); 11369f2d3eaeSJakob Unterwurzacher if (!urb) 11379f2d3eaeSJakob Unterwurzacher goto drop; 11389f2d3eaeSJakob Unterwurzacher 11399f2d3eaeSJakob Unterwurzacher /* put the skb on can loopback stack */ 11409f2d3eaeSJakob Unterwurzacher spin_lock_irqsave(&up->echo_skb_lock, flags); 11411dcb6e57SVincent Mailhol can_put_echo_skb(skb, up->netdev, echo_index, 0); 11429f2d3eaeSJakob Unterwurzacher spin_unlock_irqrestore(&up->echo_skb_lock, flags); 11439f2d3eaeSJakob Unterwurzacher 11449f2d3eaeSJakob Unterwurzacher /* transmit it */ 11459f2d3eaeSJakob Unterwurzacher usb_anchor_urb(urb, &up->tx_urbs); 11469f2d3eaeSJakob Unterwurzacher ret = usb_submit_urb(urb, GFP_ATOMIC); 11479f2d3eaeSJakob Unterwurzacher 11489f2d3eaeSJakob Unterwurzacher /* cleanup urb */ 11499f2d3eaeSJakob Unterwurzacher if (ret) { 11509f2d3eaeSJakob Unterwurzacher /* on error, clean up */ 11519f2d3eaeSJakob Unterwurzacher usb_unanchor_urb(urb); 11529f2d3eaeSJakob Unterwurzacher ucan_clean_up_tx_urb(up, urb); 11539f2d3eaeSJakob Unterwurzacher if (!ucan_release_context(up, context)) 11549f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 11559f2d3eaeSJakob Unterwurzacher "xmit err: failed to release context\n"); 11569f2d3eaeSJakob Unterwurzacher 11579f2d3eaeSJakob Unterwurzacher /* remove the skb from the echo stack - this also 11589f2d3eaeSJakob Unterwurzacher * frees the skb 11599f2d3eaeSJakob Unterwurzacher */ 11609f2d3eaeSJakob Unterwurzacher spin_lock_irqsave(&up->echo_skb_lock, flags); 1161f318482aSMarc Kleine-Budde can_free_echo_skb(up->netdev, echo_index, NULL); 11629f2d3eaeSJakob Unterwurzacher spin_unlock_irqrestore(&up->echo_skb_lock, flags); 11639f2d3eaeSJakob Unterwurzacher 11649f2d3eaeSJakob Unterwurzacher if (ret == -ENODEV) { 11659f2d3eaeSJakob Unterwurzacher netif_device_detach(up->netdev); 11669f2d3eaeSJakob Unterwurzacher } else { 11679f2d3eaeSJakob Unterwurzacher netdev_warn(up->netdev, 11689f2d3eaeSJakob Unterwurzacher "xmit err: failed to submit urb %d\n", 11699f2d3eaeSJakob Unterwurzacher ret); 11709f2d3eaeSJakob Unterwurzacher up->netdev->stats.tx_dropped++; 11719f2d3eaeSJakob Unterwurzacher } 11729f2d3eaeSJakob Unterwurzacher return NETDEV_TX_OK; 11739f2d3eaeSJakob Unterwurzacher } 11749f2d3eaeSJakob Unterwurzacher 11759f2d3eaeSJakob Unterwurzacher netif_trans_update(netdev); 11769f2d3eaeSJakob Unterwurzacher 11779f2d3eaeSJakob Unterwurzacher /* release ref, as we do not need the urb anymore */ 11789f2d3eaeSJakob Unterwurzacher usb_free_urb(urb); 11799f2d3eaeSJakob Unterwurzacher 11809f2d3eaeSJakob Unterwurzacher return NETDEV_TX_OK; 11819f2d3eaeSJakob Unterwurzacher 11829f2d3eaeSJakob Unterwurzacher drop: 11839f2d3eaeSJakob Unterwurzacher if (!ucan_release_context(up, context)) 11849f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 11859f2d3eaeSJakob Unterwurzacher "xmit drop: failed to release context\n"); 11869f2d3eaeSJakob Unterwurzacher dev_kfree_skb(skb); 11879f2d3eaeSJakob Unterwurzacher up->netdev->stats.tx_dropped++; 11889f2d3eaeSJakob Unterwurzacher 11899f2d3eaeSJakob Unterwurzacher return NETDEV_TX_OK; 11909f2d3eaeSJakob Unterwurzacher } 11919f2d3eaeSJakob Unterwurzacher 11929f2d3eaeSJakob Unterwurzacher /* Device goes down 11939f2d3eaeSJakob Unterwurzacher * 11949f2d3eaeSJakob Unterwurzacher * Clean up used resources 11959f2d3eaeSJakob Unterwurzacher */ 11969f2d3eaeSJakob Unterwurzacher static int ucan_close(struct net_device *netdev) 11979f2d3eaeSJakob Unterwurzacher { 11989f2d3eaeSJakob Unterwurzacher int ret; 11999f2d3eaeSJakob Unterwurzacher struct ucan_priv *up = netdev_priv(netdev); 12009f2d3eaeSJakob Unterwurzacher 12019f2d3eaeSJakob Unterwurzacher up->can.state = CAN_STATE_STOPPED; 12029f2d3eaeSJakob Unterwurzacher 12039f2d3eaeSJakob Unterwurzacher /* stop sending data */ 12049f2d3eaeSJakob Unterwurzacher usb_kill_anchored_urbs(&up->tx_urbs); 12059f2d3eaeSJakob Unterwurzacher 12069f2d3eaeSJakob Unterwurzacher /* stop receiving data */ 12079f2d3eaeSJakob Unterwurzacher usb_kill_anchored_urbs(&up->rx_urbs); 12089f2d3eaeSJakob Unterwurzacher 12099f2d3eaeSJakob Unterwurzacher /* stop and reset can device */ 12109f2d3eaeSJakob Unterwurzacher ret = ucan_ctrl_command_out(up, UCAN_COMMAND_STOP, 0, 0); 12119f2d3eaeSJakob Unterwurzacher if (ret < 0) 12129f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 12139f2d3eaeSJakob Unterwurzacher "could not stop device, code: %d\n", 12149f2d3eaeSJakob Unterwurzacher ret); 12159f2d3eaeSJakob Unterwurzacher 12169f2d3eaeSJakob Unterwurzacher ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); 12179f2d3eaeSJakob Unterwurzacher if (ret < 0) 12189f2d3eaeSJakob Unterwurzacher netdev_err(up->netdev, 12199f2d3eaeSJakob Unterwurzacher "could not reset device, code: %d\n", 12209f2d3eaeSJakob Unterwurzacher ret); 12219f2d3eaeSJakob Unterwurzacher 12229f2d3eaeSJakob Unterwurzacher netif_stop_queue(netdev); 12239f2d3eaeSJakob Unterwurzacher 12249f2d3eaeSJakob Unterwurzacher ucan_release_context_array(up); 12259f2d3eaeSJakob Unterwurzacher 12269f2d3eaeSJakob Unterwurzacher close_candev(up->netdev); 12279f2d3eaeSJakob Unterwurzacher return 0; 12289f2d3eaeSJakob Unterwurzacher } 12299f2d3eaeSJakob Unterwurzacher 12309f2d3eaeSJakob Unterwurzacher /* CAN driver callbacks */ 12319f2d3eaeSJakob Unterwurzacher static const struct net_device_ops ucan_netdev_ops = { 12329f2d3eaeSJakob Unterwurzacher .ndo_open = ucan_open, 12339f2d3eaeSJakob Unterwurzacher .ndo_stop = ucan_close, 12349f2d3eaeSJakob Unterwurzacher .ndo_start_xmit = ucan_start_xmit, 12359f2d3eaeSJakob Unterwurzacher .ndo_change_mtu = can_change_mtu, 12369f2d3eaeSJakob Unterwurzacher }; 12379f2d3eaeSJakob Unterwurzacher 1238409c188cSVincent Mailhol static const struct ethtool_ops ucan_ethtool_ops = { 1239409c188cSVincent Mailhol .get_ts_info = ethtool_op_get_ts_info, 1240409c188cSVincent Mailhol }; 1241409c188cSVincent Mailhol 12429f2d3eaeSJakob Unterwurzacher /* Request to set bittiming 12439f2d3eaeSJakob Unterwurzacher * 12449f2d3eaeSJakob Unterwurzacher * This function generates an USB set bittiming message and transmits 12459f2d3eaeSJakob Unterwurzacher * it to the device 12469f2d3eaeSJakob Unterwurzacher */ 12479f2d3eaeSJakob Unterwurzacher static int ucan_set_bittiming(struct net_device *netdev) 12489f2d3eaeSJakob Unterwurzacher { 12499f2d3eaeSJakob Unterwurzacher int ret; 12509f2d3eaeSJakob Unterwurzacher struct ucan_priv *up = netdev_priv(netdev); 12519f2d3eaeSJakob Unterwurzacher struct ucan_ctl_cmd_set_bittiming *cmd_set_bittiming; 12529f2d3eaeSJakob Unterwurzacher 12539f2d3eaeSJakob Unterwurzacher cmd_set_bittiming = &up->ctl_msg_buffer->cmd_set_bittiming; 12549f2d3eaeSJakob Unterwurzacher cmd_set_bittiming->tq = cpu_to_le32(up->can.bittiming.tq); 12559f2d3eaeSJakob Unterwurzacher cmd_set_bittiming->brp = cpu_to_le16(up->can.bittiming.brp); 12569f2d3eaeSJakob Unterwurzacher cmd_set_bittiming->sample_point = 12579f2d3eaeSJakob Unterwurzacher cpu_to_le16(up->can.bittiming.sample_point); 12589f2d3eaeSJakob Unterwurzacher cmd_set_bittiming->prop_seg = up->can.bittiming.prop_seg; 12599f2d3eaeSJakob Unterwurzacher cmd_set_bittiming->phase_seg1 = up->can.bittiming.phase_seg1; 12609f2d3eaeSJakob Unterwurzacher cmd_set_bittiming->phase_seg2 = up->can.bittiming.phase_seg2; 12619f2d3eaeSJakob Unterwurzacher cmd_set_bittiming->sjw = up->can.bittiming.sjw; 12629f2d3eaeSJakob Unterwurzacher 12639f2d3eaeSJakob Unterwurzacher ret = ucan_ctrl_command_out(up, UCAN_COMMAND_SET_BITTIMING, 0, 12649f2d3eaeSJakob Unterwurzacher sizeof(*cmd_set_bittiming)); 12659f2d3eaeSJakob Unterwurzacher return (ret < 0) ? ret : 0; 12669f2d3eaeSJakob Unterwurzacher } 12679f2d3eaeSJakob Unterwurzacher 12689f2d3eaeSJakob Unterwurzacher /* Restart the device to get it out of BUS-OFF state. 12699f2d3eaeSJakob Unterwurzacher * Called when the user runs "ip link set can1 type can restart". 12709f2d3eaeSJakob Unterwurzacher */ 12719f2d3eaeSJakob Unterwurzacher static int ucan_set_mode(struct net_device *netdev, enum can_mode mode) 12729f2d3eaeSJakob Unterwurzacher { 12739f2d3eaeSJakob Unterwurzacher int ret; 12749f2d3eaeSJakob Unterwurzacher unsigned long flags; 12759f2d3eaeSJakob Unterwurzacher struct ucan_priv *up = netdev_priv(netdev); 12769f2d3eaeSJakob Unterwurzacher 12779f2d3eaeSJakob Unterwurzacher switch (mode) { 12789f2d3eaeSJakob Unterwurzacher case CAN_MODE_START: 12799f2d3eaeSJakob Unterwurzacher netdev_dbg(up->netdev, "restarting device\n"); 12809f2d3eaeSJakob Unterwurzacher 12819f2d3eaeSJakob Unterwurzacher ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESTART, 0, 0); 12829f2d3eaeSJakob Unterwurzacher up->can.state = CAN_STATE_ERROR_ACTIVE; 12839f2d3eaeSJakob Unterwurzacher 12849f2d3eaeSJakob Unterwurzacher /* check if queue can be restarted, 12859f2d3eaeSJakob Unterwurzacher * up->available_tx_urbs must be protected by the 12869f2d3eaeSJakob Unterwurzacher * lock 12879f2d3eaeSJakob Unterwurzacher */ 12889f2d3eaeSJakob Unterwurzacher spin_lock_irqsave(&up->context_lock, flags); 12899f2d3eaeSJakob Unterwurzacher 12909f2d3eaeSJakob Unterwurzacher if (up->available_tx_urbs > 0) 12919f2d3eaeSJakob Unterwurzacher netif_wake_queue(up->netdev); 12929f2d3eaeSJakob Unterwurzacher 12939f2d3eaeSJakob Unterwurzacher spin_unlock_irqrestore(&up->context_lock, flags); 12949f2d3eaeSJakob Unterwurzacher 12959f2d3eaeSJakob Unterwurzacher return ret; 12969f2d3eaeSJakob Unterwurzacher default: 12979f2d3eaeSJakob Unterwurzacher return -EOPNOTSUPP; 12989f2d3eaeSJakob Unterwurzacher } 12999f2d3eaeSJakob Unterwurzacher } 13009f2d3eaeSJakob Unterwurzacher 13019f2d3eaeSJakob Unterwurzacher /* Probe the device, reset it and gather general device information */ 13029f2d3eaeSJakob Unterwurzacher static int ucan_probe(struct usb_interface *intf, 13039f2d3eaeSJakob Unterwurzacher const struct usb_device_id *id) 13049f2d3eaeSJakob Unterwurzacher { 13059f2d3eaeSJakob Unterwurzacher int ret; 13069f2d3eaeSJakob Unterwurzacher int i; 13079f2d3eaeSJakob Unterwurzacher u32 protocol_version; 13089f2d3eaeSJakob Unterwurzacher struct usb_device *udev; 13099f2d3eaeSJakob Unterwurzacher struct net_device *netdev; 13109f2d3eaeSJakob Unterwurzacher struct usb_host_interface *iface_desc; 13119f2d3eaeSJakob Unterwurzacher struct ucan_priv *up; 13129f2d3eaeSJakob Unterwurzacher struct usb_endpoint_descriptor *ep; 13139f2d3eaeSJakob Unterwurzacher u16 in_ep_size; 13149f2d3eaeSJakob Unterwurzacher u16 out_ep_size; 13159f2d3eaeSJakob Unterwurzacher u8 in_ep_addr; 13169f2d3eaeSJakob Unterwurzacher u8 out_ep_addr; 13179f2d3eaeSJakob Unterwurzacher union ucan_ctl_payload *ctl_msg_buffer; 13189f2d3eaeSJakob Unterwurzacher char firmware_str[sizeof(union ucan_ctl_payload) + 1]; 13199f2d3eaeSJakob Unterwurzacher 13209f2d3eaeSJakob Unterwurzacher udev = interface_to_usbdev(intf); 13219f2d3eaeSJakob Unterwurzacher 13229f2d3eaeSJakob Unterwurzacher /* Stage 1 - Interface Parsing 13239f2d3eaeSJakob Unterwurzacher * --------------------------- 13249f2d3eaeSJakob Unterwurzacher * 13259f2d3eaeSJakob Unterwurzacher * Identifie the device USB interface descriptor and its 13269f2d3eaeSJakob Unterwurzacher * endpoints. Probing is aborted on errors. 13279f2d3eaeSJakob Unterwurzacher */ 13289f2d3eaeSJakob Unterwurzacher 13299f2d3eaeSJakob Unterwurzacher /* check if the interface is sane */ 13309f2d3eaeSJakob Unterwurzacher iface_desc = intf->cur_altsetting; 13319f2d3eaeSJakob Unterwurzacher if (!iface_desc) 13329f2d3eaeSJakob Unterwurzacher return -ENODEV; 13339f2d3eaeSJakob Unterwurzacher 13349f2d3eaeSJakob Unterwurzacher dev_info(&udev->dev, 13359f2d3eaeSJakob Unterwurzacher "%s: probing device on interface #%d\n", 13369f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME, 13379f2d3eaeSJakob Unterwurzacher iface_desc->desc.bInterfaceNumber); 13389f2d3eaeSJakob Unterwurzacher 13399f2d3eaeSJakob Unterwurzacher /* interface sanity check */ 13409f2d3eaeSJakob Unterwurzacher if (iface_desc->desc.bNumEndpoints != 2) { 13419f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, 13429f2d3eaeSJakob Unterwurzacher "%s: invalid EP count (%d)", 13439f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME, iface_desc->desc.bNumEndpoints); 13449f2d3eaeSJakob Unterwurzacher goto err_firmware_needs_update; 13459f2d3eaeSJakob Unterwurzacher } 13469f2d3eaeSJakob Unterwurzacher 13479f2d3eaeSJakob Unterwurzacher /* check interface endpoints */ 13489f2d3eaeSJakob Unterwurzacher in_ep_addr = 0; 13499f2d3eaeSJakob Unterwurzacher out_ep_addr = 0; 13509f2d3eaeSJakob Unterwurzacher in_ep_size = 0; 13519f2d3eaeSJakob Unterwurzacher out_ep_size = 0; 13529f2d3eaeSJakob Unterwurzacher for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { 13539f2d3eaeSJakob Unterwurzacher ep = &iface_desc->endpoint[i].desc; 13549f2d3eaeSJakob Unterwurzacher 13559f2d3eaeSJakob Unterwurzacher if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0) && 13569f2d3eaeSJakob Unterwurzacher ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 13579f2d3eaeSJakob Unterwurzacher USB_ENDPOINT_XFER_BULK)) { 13589f2d3eaeSJakob Unterwurzacher /* In Endpoint */ 13599f2d3eaeSJakob Unterwurzacher in_ep_addr = ep->bEndpointAddress; 13609f2d3eaeSJakob Unterwurzacher in_ep_addr &= USB_ENDPOINT_NUMBER_MASK; 13619f2d3eaeSJakob Unterwurzacher in_ep_size = le16_to_cpu(ep->wMaxPacketSize); 13629f2d3eaeSJakob Unterwurzacher } else if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 13639f2d3eaeSJakob Unterwurzacher 0) && 13649f2d3eaeSJakob Unterwurzacher ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 13659f2d3eaeSJakob Unterwurzacher USB_ENDPOINT_XFER_BULK)) { 13669f2d3eaeSJakob Unterwurzacher /* Out Endpoint */ 13679f2d3eaeSJakob Unterwurzacher out_ep_addr = ep->bEndpointAddress; 13689f2d3eaeSJakob Unterwurzacher out_ep_addr &= USB_ENDPOINT_NUMBER_MASK; 13699f2d3eaeSJakob Unterwurzacher out_ep_size = le16_to_cpu(ep->wMaxPacketSize); 13709f2d3eaeSJakob Unterwurzacher } 13719f2d3eaeSJakob Unterwurzacher } 13729f2d3eaeSJakob Unterwurzacher 13739f2d3eaeSJakob Unterwurzacher /* check if interface is sane */ 13749f2d3eaeSJakob Unterwurzacher if (!in_ep_addr || !out_ep_addr) { 13759f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, "%s: invalid endpoint configuration\n", 13769f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME); 13779f2d3eaeSJakob Unterwurzacher goto err_firmware_needs_update; 13789f2d3eaeSJakob Unterwurzacher } 13799f2d3eaeSJakob Unterwurzacher if (in_ep_size < sizeof(struct ucan_message_in)) { 13809f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, "%s: invalid in_ep MaxPacketSize\n", 13819f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME); 13829f2d3eaeSJakob Unterwurzacher goto err_firmware_needs_update; 13839f2d3eaeSJakob Unterwurzacher } 13849f2d3eaeSJakob Unterwurzacher if (out_ep_size < sizeof(struct ucan_message_out)) { 13859f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, "%s: invalid out_ep MaxPacketSize\n", 13869f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME); 13879f2d3eaeSJakob Unterwurzacher goto err_firmware_needs_update; 13889f2d3eaeSJakob Unterwurzacher } 13899f2d3eaeSJakob Unterwurzacher 13909f2d3eaeSJakob Unterwurzacher /* Stage 2 - Device Identification 13919f2d3eaeSJakob Unterwurzacher * ------------------------------- 13929f2d3eaeSJakob Unterwurzacher * 13939f2d3eaeSJakob Unterwurzacher * The device interface seems to be a ucan device. Do further 13949f2d3eaeSJakob Unterwurzacher * compatibility checks. On error probing is aborted, on 13959f2d3eaeSJakob Unterwurzacher * success this stage leaves the ctl_msg_buffer with the 13969f2d3eaeSJakob Unterwurzacher * reported contents of a GET_INFO command (supported 13979f2d3eaeSJakob Unterwurzacher * bittimings, tx_fifo depth). This information is used in 13989f2d3eaeSJakob Unterwurzacher * Stage 3 for the final driver initialisation. 13999f2d3eaeSJakob Unterwurzacher */ 14009f2d3eaeSJakob Unterwurzacher 1401c34983c9SJulia Lawall /* Prepare Memory for control transfers */ 14029f2d3eaeSJakob Unterwurzacher ctl_msg_buffer = devm_kzalloc(&udev->dev, 14039f2d3eaeSJakob Unterwurzacher sizeof(union ucan_ctl_payload), 14049f2d3eaeSJakob Unterwurzacher GFP_KERNEL); 14059f2d3eaeSJakob Unterwurzacher if (!ctl_msg_buffer) { 14069f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, 14079f2d3eaeSJakob Unterwurzacher "%s: failed to allocate control pipe memory\n", 14089f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME); 14099f2d3eaeSJakob Unterwurzacher return -ENOMEM; 14109f2d3eaeSJakob Unterwurzacher } 14119f2d3eaeSJakob Unterwurzacher 14129f2d3eaeSJakob Unterwurzacher /* get protocol version 14139f2d3eaeSJakob Unterwurzacher * 14149f2d3eaeSJakob Unterwurzacher * note: ucan_ctrl_command_* wrappers cannot be used yet 14159f2d3eaeSJakob Unterwurzacher * because `up` is initialised in Stage 3 14169f2d3eaeSJakob Unterwurzacher */ 14179f2d3eaeSJakob Unterwurzacher ret = usb_control_msg(udev, 14189f2d3eaeSJakob Unterwurzacher usb_rcvctrlpipe(udev, 0), 14199f2d3eaeSJakob Unterwurzacher UCAN_COMMAND_GET, 14209f2d3eaeSJakob Unterwurzacher USB_DIR_IN | USB_TYPE_VENDOR | 14219f2d3eaeSJakob Unterwurzacher USB_RECIP_INTERFACE, 14229f2d3eaeSJakob Unterwurzacher UCAN_COMMAND_GET_PROTOCOL_VERSION, 14239f2d3eaeSJakob Unterwurzacher iface_desc->desc.bInterfaceNumber, 14249f2d3eaeSJakob Unterwurzacher ctl_msg_buffer, 14259f2d3eaeSJakob Unterwurzacher sizeof(union ucan_ctl_payload), 14269f2d3eaeSJakob Unterwurzacher UCAN_USB_CTL_PIPE_TIMEOUT); 14279f2d3eaeSJakob Unterwurzacher 14289f2d3eaeSJakob Unterwurzacher /* older firmware version do not support this command - those 14299f2d3eaeSJakob Unterwurzacher * are not supported by this drive 14309f2d3eaeSJakob Unterwurzacher */ 14319f2d3eaeSJakob Unterwurzacher if (ret != 4) { 14329f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, 14339f2d3eaeSJakob Unterwurzacher "%s: could not read protocol version, ret=%d\n", 14349f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME, ret); 14359f2d3eaeSJakob Unterwurzacher if (ret >= 0) 14369f2d3eaeSJakob Unterwurzacher ret = -EINVAL; 14379f2d3eaeSJakob Unterwurzacher goto err_firmware_needs_update; 14389f2d3eaeSJakob Unterwurzacher } 14399f2d3eaeSJakob Unterwurzacher 14409f2d3eaeSJakob Unterwurzacher /* this driver currently supports protocol version 3 only */ 14419f2d3eaeSJakob Unterwurzacher protocol_version = 14429f2d3eaeSJakob Unterwurzacher le32_to_cpu(ctl_msg_buffer->cmd_get_protocol_version.version); 14439f2d3eaeSJakob Unterwurzacher if (protocol_version < UCAN_PROTOCOL_VERSION_MIN || 14449f2d3eaeSJakob Unterwurzacher protocol_version > UCAN_PROTOCOL_VERSION_MAX) { 14459f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, 14469f2d3eaeSJakob Unterwurzacher "%s: device protocol version %d is not supported\n", 14479f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME, protocol_version); 14489f2d3eaeSJakob Unterwurzacher goto err_firmware_needs_update; 14499f2d3eaeSJakob Unterwurzacher } 14509f2d3eaeSJakob Unterwurzacher 14519f2d3eaeSJakob Unterwurzacher /* request the device information and store it in ctl_msg_buffer 14529f2d3eaeSJakob Unterwurzacher * 145388bfb9a7SMarc Kleine-Budde * note: ucan_ctrl_command_* wrappers cannot be used yet 14549f2d3eaeSJakob Unterwurzacher * because `up` is initialised in Stage 3 14559f2d3eaeSJakob Unterwurzacher */ 14569f2d3eaeSJakob Unterwurzacher ret = usb_control_msg(udev, 14579f2d3eaeSJakob Unterwurzacher usb_rcvctrlpipe(udev, 0), 14589f2d3eaeSJakob Unterwurzacher UCAN_COMMAND_GET, 14599f2d3eaeSJakob Unterwurzacher USB_DIR_IN | USB_TYPE_VENDOR | 14609f2d3eaeSJakob Unterwurzacher USB_RECIP_INTERFACE, 14619f2d3eaeSJakob Unterwurzacher UCAN_COMMAND_GET_INFO, 14629f2d3eaeSJakob Unterwurzacher iface_desc->desc.bInterfaceNumber, 14639f2d3eaeSJakob Unterwurzacher ctl_msg_buffer, 14649f2d3eaeSJakob Unterwurzacher sizeof(ctl_msg_buffer->cmd_get_device_info), 14659f2d3eaeSJakob Unterwurzacher UCAN_USB_CTL_PIPE_TIMEOUT); 14669f2d3eaeSJakob Unterwurzacher 14679f2d3eaeSJakob Unterwurzacher if (ret < 0) { 14689f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, "%s: failed to retrieve device info\n", 14699f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME); 14709f2d3eaeSJakob Unterwurzacher goto err_firmware_needs_update; 14719f2d3eaeSJakob Unterwurzacher } 14729f2d3eaeSJakob Unterwurzacher if (ret < sizeof(ctl_msg_buffer->cmd_get_device_info)) { 14739f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, "%s: device reported invalid device info\n", 14749f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME); 14759f2d3eaeSJakob Unterwurzacher goto err_firmware_needs_update; 14769f2d3eaeSJakob Unterwurzacher } 14779f2d3eaeSJakob Unterwurzacher if (ctl_msg_buffer->cmd_get_device_info.tx_fifo == 0) { 14789f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, 14799f2d3eaeSJakob Unterwurzacher "%s: device reported invalid tx-fifo size\n", 14809f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME); 14819f2d3eaeSJakob Unterwurzacher goto err_firmware_needs_update; 14829f2d3eaeSJakob Unterwurzacher } 14839f2d3eaeSJakob Unterwurzacher 14849f2d3eaeSJakob Unterwurzacher /* Stage 3 - Driver Initialisation 14859f2d3eaeSJakob Unterwurzacher * ------------------------------- 14869f2d3eaeSJakob Unterwurzacher * 14879f2d3eaeSJakob Unterwurzacher * Register device to Linux, prepare private structures and 14889f2d3eaeSJakob Unterwurzacher * reset the device. 14899f2d3eaeSJakob Unterwurzacher */ 14909f2d3eaeSJakob Unterwurzacher 14919f2d3eaeSJakob Unterwurzacher /* allocate driver resources */ 14929f2d3eaeSJakob Unterwurzacher netdev = alloc_candev(sizeof(struct ucan_priv), 14939f2d3eaeSJakob Unterwurzacher ctl_msg_buffer->cmd_get_device_info.tx_fifo); 14949f2d3eaeSJakob Unterwurzacher if (!netdev) { 14959f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, 14969f2d3eaeSJakob Unterwurzacher "%s: cannot allocate candev\n", UCAN_DRIVER_NAME); 14979f2d3eaeSJakob Unterwurzacher return -ENOMEM; 14989f2d3eaeSJakob Unterwurzacher } 14999f2d3eaeSJakob Unterwurzacher 15009f2d3eaeSJakob Unterwurzacher up = netdev_priv(netdev); 15019f2d3eaeSJakob Unterwurzacher 150288bfb9a7SMarc Kleine-Budde /* initialize data */ 15039f2d3eaeSJakob Unterwurzacher up->udev = udev; 15049f2d3eaeSJakob Unterwurzacher up->intf = intf; 15059f2d3eaeSJakob Unterwurzacher up->netdev = netdev; 15069f2d3eaeSJakob Unterwurzacher up->intf_index = iface_desc->desc.bInterfaceNumber; 15079f2d3eaeSJakob Unterwurzacher up->in_ep_addr = in_ep_addr; 15089f2d3eaeSJakob Unterwurzacher up->out_ep_addr = out_ep_addr; 15099f2d3eaeSJakob Unterwurzacher up->in_ep_size = in_ep_size; 15109f2d3eaeSJakob Unterwurzacher up->ctl_msg_buffer = ctl_msg_buffer; 15119f2d3eaeSJakob Unterwurzacher up->context_array = NULL; 15129f2d3eaeSJakob Unterwurzacher up->available_tx_urbs = 0; 15139f2d3eaeSJakob Unterwurzacher 15149f2d3eaeSJakob Unterwurzacher up->can.state = CAN_STATE_STOPPED; 15159f2d3eaeSJakob Unterwurzacher up->can.bittiming_const = &up->device_info.bittiming_const; 15169f2d3eaeSJakob Unterwurzacher up->can.do_set_bittiming = ucan_set_bittiming; 15179f2d3eaeSJakob Unterwurzacher up->can.do_set_mode = &ucan_set_mode; 15189f2d3eaeSJakob Unterwurzacher spin_lock_init(&up->context_lock); 15199f2d3eaeSJakob Unterwurzacher spin_lock_init(&up->echo_skb_lock); 15209f2d3eaeSJakob Unterwurzacher netdev->netdev_ops = &ucan_netdev_ops; 1521409c188cSVincent Mailhol netdev->ethtool_ops = &ucan_ethtool_ops; 15229f2d3eaeSJakob Unterwurzacher 15239f2d3eaeSJakob Unterwurzacher usb_set_intfdata(intf, up); 15249f2d3eaeSJakob Unterwurzacher SET_NETDEV_DEV(netdev, &intf->dev); 15259f2d3eaeSJakob Unterwurzacher 15269f2d3eaeSJakob Unterwurzacher /* parse device information 15279f2d3eaeSJakob Unterwurzacher * the data retrieved in Stage 2 is still available in 15289f2d3eaeSJakob Unterwurzacher * up->ctl_msg_buffer 15299f2d3eaeSJakob Unterwurzacher */ 15309f2d3eaeSJakob Unterwurzacher ucan_parse_device_info(up, &ctl_msg_buffer->cmd_get_device_info); 15319f2d3eaeSJakob Unterwurzacher 15329f2d3eaeSJakob Unterwurzacher /* just print some device information - if available */ 15339f2d3eaeSJakob Unterwurzacher ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0, 15349f2d3eaeSJakob Unterwurzacher sizeof(union ucan_ctl_payload)); 15359f2d3eaeSJakob Unterwurzacher if (ret > 0) { 1536c34983c9SJulia Lawall /* copy string while ensuring zero termination */ 15379f2d3eaeSJakob Unterwurzacher strncpy(firmware_str, up->ctl_msg_buffer->raw, 15389f2d3eaeSJakob Unterwurzacher sizeof(union ucan_ctl_payload)); 15399f2d3eaeSJakob Unterwurzacher firmware_str[sizeof(union ucan_ctl_payload)] = '\0'; 15409f2d3eaeSJakob Unterwurzacher } else { 15419f2d3eaeSJakob Unterwurzacher strcpy(firmware_str, "unknown"); 15429f2d3eaeSJakob Unterwurzacher } 15439f2d3eaeSJakob Unterwurzacher 15449f2d3eaeSJakob Unterwurzacher /* device is compatible, reset it */ 15459f2d3eaeSJakob Unterwurzacher ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); 15469f2d3eaeSJakob Unterwurzacher if (ret < 0) 15479f2d3eaeSJakob Unterwurzacher goto err_free_candev; 15489f2d3eaeSJakob Unterwurzacher 15499f2d3eaeSJakob Unterwurzacher init_usb_anchor(&up->rx_urbs); 15509f2d3eaeSJakob Unterwurzacher init_usb_anchor(&up->tx_urbs); 15519f2d3eaeSJakob Unterwurzacher 15529f2d3eaeSJakob Unterwurzacher up->can.state = CAN_STATE_STOPPED; 15539f2d3eaeSJakob Unterwurzacher 15549f2d3eaeSJakob Unterwurzacher /* register the device */ 15559f2d3eaeSJakob Unterwurzacher ret = register_candev(netdev); 15569f2d3eaeSJakob Unterwurzacher if (ret) 15579f2d3eaeSJakob Unterwurzacher goto err_free_candev; 15589f2d3eaeSJakob Unterwurzacher 15599f2d3eaeSJakob Unterwurzacher /* initialisation complete, log device info */ 15609f2d3eaeSJakob Unterwurzacher netdev_info(up->netdev, "registered device\n"); 15619f2d3eaeSJakob Unterwurzacher netdev_info(up->netdev, "firmware string: %s\n", firmware_str); 15629f2d3eaeSJakob Unterwurzacher 15639f2d3eaeSJakob Unterwurzacher /* success */ 15649f2d3eaeSJakob Unterwurzacher return 0; 15659f2d3eaeSJakob Unterwurzacher 15669f2d3eaeSJakob Unterwurzacher err_free_candev: 15679f2d3eaeSJakob Unterwurzacher free_candev(netdev); 15689f2d3eaeSJakob Unterwurzacher return ret; 15699f2d3eaeSJakob Unterwurzacher 15709f2d3eaeSJakob Unterwurzacher err_firmware_needs_update: 15719f2d3eaeSJakob Unterwurzacher dev_err(&udev->dev, 15729f2d3eaeSJakob Unterwurzacher "%s: probe failed; try to update the device firmware\n", 15739f2d3eaeSJakob Unterwurzacher UCAN_DRIVER_NAME); 15749f2d3eaeSJakob Unterwurzacher return -ENODEV; 15759f2d3eaeSJakob Unterwurzacher } 15769f2d3eaeSJakob Unterwurzacher 15779f2d3eaeSJakob Unterwurzacher /* disconnect the device */ 15789f2d3eaeSJakob Unterwurzacher static void ucan_disconnect(struct usb_interface *intf) 15799f2d3eaeSJakob Unterwurzacher { 15809f2d3eaeSJakob Unterwurzacher struct ucan_priv *up = usb_get_intfdata(intf); 15819f2d3eaeSJakob Unterwurzacher 15829f2d3eaeSJakob Unterwurzacher usb_set_intfdata(intf, NULL); 15839f2d3eaeSJakob Unterwurzacher 15849f2d3eaeSJakob Unterwurzacher if (up) { 1585*aa9832e4SDongliang Mu unregister_candev(up->netdev); 15869f2d3eaeSJakob Unterwurzacher free_candev(up->netdev); 15879f2d3eaeSJakob Unterwurzacher } 15889f2d3eaeSJakob Unterwurzacher } 15899f2d3eaeSJakob Unterwurzacher 15909f2d3eaeSJakob Unterwurzacher static struct usb_device_id ucan_table[] = { 15919f2d3eaeSJakob Unterwurzacher /* Mule (soldered onto compute modules) */ 15929f2d3eaeSJakob Unterwurzacher {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425a, 0)}, 15939f2d3eaeSJakob Unterwurzacher /* Seal (standalone USB stick) */ 15949f2d3eaeSJakob Unterwurzacher {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425b, 0)}, 15959f2d3eaeSJakob Unterwurzacher {} /* Terminating entry */ 15969f2d3eaeSJakob Unterwurzacher }; 15979f2d3eaeSJakob Unterwurzacher 15989f2d3eaeSJakob Unterwurzacher MODULE_DEVICE_TABLE(usb, ucan_table); 15999f2d3eaeSJakob Unterwurzacher /* driver callbacks */ 16009f2d3eaeSJakob Unterwurzacher static struct usb_driver ucan_driver = { 16019f2d3eaeSJakob Unterwurzacher .name = UCAN_DRIVER_NAME, 16029f2d3eaeSJakob Unterwurzacher .probe = ucan_probe, 16039f2d3eaeSJakob Unterwurzacher .disconnect = ucan_disconnect, 16049f2d3eaeSJakob Unterwurzacher .id_table = ucan_table, 16059f2d3eaeSJakob Unterwurzacher }; 16069f2d3eaeSJakob Unterwurzacher 16079f2d3eaeSJakob Unterwurzacher module_usb_driver(ucan_driver); 16089f2d3eaeSJakob Unterwurzacher 16099f2d3eaeSJakob Unterwurzacher MODULE_LICENSE("GPL v2"); 16109f2d3eaeSJakob Unterwurzacher MODULE_AUTHOR("Martin Elshuber <martin.elshuber@theobroma-systems.com>"); 16119f2d3eaeSJakob Unterwurzacher MODULE_AUTHOR("Jakob Unterwurzacher <jakob.unterwurzacher@theobroma-systems.com>"); 16129f2d3eaeSJakob Unterwurzacher MODULE_DESCRIPTION("Driver for Theobroma Systems UCAN devices"); 1613