162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* Driver for Theobroma Systems UCAN devices, Protocol Version 3 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018 Theobroma Systems Design und Consulting GmbH 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * General Description: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * The USB Device uses three Endpoints: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * CONTROL Endpoint: Is used the setup the device (start, stop, 1362306a36Sopenharmony_ci * info, configure). 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * IN Endpoint: The device sends CAN Frame Messages and Device 1662306a36Sopenharmony_ci * Information using the IN endpoint. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * OUT Endpoint: The driver sends configuration requests, and CAN 1962306a36Sopenharmony_ci * Frames on the out endpoint. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Error Handling: 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * If error reporting is turned on the device encodes error into CAN 2462306a36Sopenharmony_ci * error frames (see uapi/linux/can/error.h) and sends it using the 2562306a36Sopenharmony_ci * IN Endpoint. The driver updates statistics and forward it. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/can.h> 2962306a36Sopenharmony_ci#include <linux/can/dev.h> 3062306a36Sopenharmony_ci#include <linux/can/error.h> 3162306a36Sopenharmony_ci#include <linux/ethtool.h> 3262306a36Sopenharmony_ci#include <linux/module.h> 3362306a36Sopenharmony_ci#include <linux/netdevice.h> 3462306a36Sopenharmony_ci#include <linux/signal.h> 3562306a36Sopenharmony_ci#include <linux/skbuff.h> 3662306a36Sopenharmony_ci#include <linux/slab.h> 3762306a36Sopenharmony_ci#include <linux/usb.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define UCAN_DRIVER_NAME "ucan" 4062306a36Sopenharmony_ci#define UCAN_MAX_RX_URBS 8 4162306a36Sopenharmony_ci/* the CAN controller needs a while to enable/disable the bus */ 4262306a36Sopenharmony_ci#define UCAN_USB_CTL_PIPE_TIMEOUT 1000 4362306a36Sopenharmony_ci/* this driver currently supports protocol version 3 only */ 4462306a36Sopenharmony_ci#define UCAN_PROTOCOL_VERSION_MIN 3 4562306a36Sopenharmony_ci#define UCAN_PROTOCOL_VERSION_MAX 3 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* UCAN Message Definitions 4862306a36Sopenharmony_ci * ------------------------ 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * ucan_message_out_t and ucan_message_in_t define the messages 5162306a36Sopenharmony_ci * transmitted on the OUT and IN endpoint. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Multibyte fields are transmitted with little endianness 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * INTR Endpoint: a single uint32_t storing the current space in the fifo 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * OUT Endpoint: single message of type ucan_message_out_t is 5862306a36Sopenharmony_ci * transmitted on the out endpoint 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * IN Endpoint: multiple messages ucan_message_in_t concateted in 6162306a36Sopenharmony_ci * the following way: 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * m[n].len <=> the length if message n(including the header in bytes) 6462306a36Sopenharmony_ci * m[n] is is aligned to a 4 byte boundary, hence 6562306a36Sopenharmony_ci * offset(m[0]) := 0; 6662306a36Sopenharmony_ci * offset(m[n+1]) := offset(m[n]) + (m[n].len + 3) & 3 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * this implies that 6962306a36Sopenharmony_ci * offset(m[n]) % 4 <=> 0 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Device Global Commands */ 7362306a36Sopenharmony_cienum { 7462306a36Sopenharmony_ci UCAN_DEVICE_GET_FW_STRING = 0, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* UCAN Commands */ 7862306a36Sopenharmony_cienum { 7962306a36Sopenharmony_ci /* start the can transceiver - val defines the operation mode */ 8062306a36Sopenharmony_ci UCAN_COMMAND_START = 0, 8162306a36Sopenharmony_ci /* cancel pending transmissions and stop the can transceiver */ 8262306a36Sopenharmony_ci UCAN_COMMAND_STOP = 1, 8362306a36Sopenharmony_ci /* send can transceiver into low-power sleep mode */ 8462306a36Sopenharmony_ci UCAN_COMMAND_SLEEP = 2, 8562306a36Sopenharmony_ci /* wake up can transceiver from low-power sleep mode */ 8662306a36Sopenharmony_ci UCAN_COMMAND_WAKEUP = 3, 8762306a36Sopenharmony_ci /* reset the can transceiver */ 8862306a36Sopenharmony_ci UCAN_COMMAND_RESET = 4, 8962306a36Sopenharmony_ci /* get piece of info from the can transceiver - subcmd defines what 9062306a36Sopenharmony_ci * piece 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci UCAN_COMMAND_GET = 5, 9362306a36Sopenharmony_ci /* clear or disable hardware filter - subcmd defines which of the two */ 9462306a36Sopenharmony_ci UCAN_COMMAND_FILTER = 6, 9562306a36Sopenharmony_ci /* Setup bittiming */ 9662306a36Sopenharmony_ci UCAN_COMMAND_SET_BITTIMING = 7, 9762306a36Sopenharmony_ci /* recover from bus-off state */ 9862306a36Sopenharmony_ci UCAN_COMMAND_RESTART = 8, 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* UCAN_COMMAND_START and UCAN_COMMAND_GET_INFO operation modes (bitmap). 10262306a36Sopenharmony_ci * Undefined bits must be set to 0. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cienum { 10562306a36Sopenharmony_ci UCAN_MODE_LOOPBACK = BIT(0), 10662306a36Sopenharmony_ci UCAN_MODE_SILENT = BIT(1), 10762306a36Sopenharmony_ci UCAN_MODE_3_SAMPLES = BIT(2), 10862306a36Sopenharmony_ci UCAN_MODE_ONE_SHOT = BIT(3), 10962306a36Sopenharmony_ci UCAN_MODE_BERR_REPORT = BIT(4), 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* UCAN_COMMAND_GET subcommands */ 11362306a36Sopenharmony_cienum { 11462306a36Sopenharmony_ci UCAN_COMMAND_GET_INFO = 0, 11562306a36Sopenharmony_ci UCAN_COMMAND_GET_PROTOCOL_VERSION = 1, 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* UCAN_COMMAND_FILTER subcommands */ 11962306a36Sopenharmony_cienum { 12062306a36Sopenharmony_ci UCAN_FILTER_CLEAR = 0, 12162306a36Sopenharmony_ci UCAN_FILTER_DISABLE = 1, 12262306a36Sopenharmony_ci UCAN_FILTER_ENABLE = 2, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* OUT endpoint message types */ 12662306a36Sopenharmony_cienum { 12762306a36Sopenharmony_ci UCAN_OUT_TX = 2, /* transmit a CAN frame */ 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* IN endpoint message types */ 13162306a36Sopenharmony_cienum { 13262306a36Sopenharmony_ci UCAN_IN_TX_COMPLETE = 1, /* CAN frame transmission completed */ 13362306a36Sopenharmony_ci UCAN_IN_RX = 2, /* CAN frame received */ 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistruct ucan_ctl_cmd_start { 13762306a36Sopenharmony_ci __le16 mode; /* OR-ing any of UCAN_MODE_* */ 13862306a36Sopenharmony_ci} __packed; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct ucan_ctl_cmd_set_bittiming { 14162306a36Sopenharmony_ci __le32 tq; /* Time quanta (TQ) in nanoseconds */ 14262306a36Sopenharmony_ci __le16 brp; /* TQ Prescaler */ 14362306a36Sopenharmony_ci __le16 sample_point; /* Samplepoint on tenth percent */ 14462306a36Sopenharmony_ci u8 prop_seg; /* Propagation segment in TQs */ 14562306a36Sopenharmony_ci u8 phase_seg1; /* Phase buffer segment 1 in TQs */ 14662306a36Sopenharmony_ci u8 phase_seg2; /* Phase buffer segment 2 in TQs */ 14762306a36Sopenharmony_ci u8 sjw; /* Synchronisation jump width in TQs */ 14862306a36Sopenharmony_ci} __packed; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct ucan_ctl_cmd_device_info { 15162306a36Sopenharmony_ci __le32 freq; /* Clock Frequency for tq generation */ 15262306a36Sopenharmony_ci u8 tx_fifo; /* Size of the transmission fifo */ 15362306a36Sopenharmony_ci u8 sjw_max; /* can_bittiming fields... */ 15462306a36Sopenharmony_ci u8 tseg1_min; 15562306a36Sopenharmony_ci u8 tseg1_max; 15662306a36Sopenharmony_ci u8 tseg2_min; 15762306a36Sopenharmony_ci u8 tseg2_max; 15862306a36Sopenharmony_ci __le16 brp_inc; 15962306a36Sopenharmony_ci __le32 brp_min; 16062306a36Sopenharmony_ci __le32 brp_max; /* ...can_bittiming fields */ 16162306a36Sopenharmony_ci __le16 ctrlmodes; /* supported control modes */ 16262306a36Sopenharmony_ci __le16 hwfilter; /* Number of HW filter banks */ 16362306a36Sopenharmony_ci __le16 rxmboxes; /* Number of receive Mailboxes */ 16462306a36Sopenharmony_ci} __packed; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistruct ucan_ctl_cmd_get_protocol_version { 16762306a36Sopenharmony_ci __le32 version; 16862306a36Sopenharmony_ci} __packed; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ciunion ucan_ctl_payload { 17162306a36Sopenharmony_ci /* Setup Bittiming 17262306a36Sopenharmony_ci * bmRequest == UCAN_COMMAND_START 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci struct ucan_ctl_cmd_start cmd_start; 17562306a36Sopenharmony_ci /* Setup Bittiming 17662306a36Sopenharmony_ci * bmRequest == UCAN_COMMAND_SET_BITTIMING 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci struct ucan_ctl_cmd_set_bittiming cmd_set_bittiming; 17962306a36Sopenharmony_ci /* Get Device Information 18062306a36Sopenharmony_ci * bmRequest == UCAN_COMMAND_GET; wValue = UCAN_COMMAND_GET_INFO 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci struct ucan_ctl_cmd_device_info cmd_get_device_info; 18362306a36Sopenharmony_ci /* Get Protocol Version 18462306a36Sopenharmony_ci * bmRequest == UCAN_COMMAND_GET; 18562306a36Sopenharmony_ci * wValue = UCAN_COMMAND_GET_PROTOCOL_VERSION 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci struct ucan_ctl_cmd_get_protocol_version cmd_get_protocol_version; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci u8 raw[128]; 19062306a36Sopenharmony_ci} __packed; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cienum { 19362306a36Sopenharmony_ci UCAN_TX_COMPLETE_SUCCESS = BIT(0), 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* Transmission Complete within ucan_message_in */ 19762306a36Sopenharmony_cistruct ucan_tx_complete_entry_t { 19862306a36Sopenharmony_ci u8 echo_index; 19962306a36Sopenharmony_ci u8 flags; 20062306a36Sopenharmony_ci} __packed __aligned(0x2); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* CAN Data message format within ucan_message_in/out */ 20362306a36Sopenharmony_cistruct ucan_can_msg { 20462306a36Sopenharmony_ci /* note DLC is computed by 20562306a36Sopenharmony_ci * msg.len - sizeof (msg.len) 20662306a36Sopenharmony_ci * - sizeof (msg.type) 20762306a36Sopenharmony_ci * - sizeof (msg.can_msg.id) 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci __le32 id; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci union { 21262306a36Sopenharmony_ci u8 data[CAN_MAX_DLEN]; /* Data of CAN frames */ 21362306a36Sopenharmony_ci u8 dlc; /* RTR dlc */ 21462306a36Sopenharmony_ci }; 21562306a36Sopenharmony_ci} __packed; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* OUT Endpoint, outbound messages */ 21862306a36Sopenharmony_cistruct ucan_message_out { 21962306a36Sopenharmony_ci __le16 len; /* Length of the content include header */ 22062306a36Sopenharmony_ci u8 type; /* UCAN_OUT_TX and friends */ 22162306a36Sopenharmony_ci u8 subtype; /* command sub type */ 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci union { 22462306a36Sopenharmony_ci /* Transmit CAN frame 22562306a36Sopenharmony_ci * (type == UCAN_TX) && ((msg.can_msg.id & CAN_RTR_FLAG) == 0) 22662306a36Sopenharmony_ci * subtype stores the echo id 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci struct ucan_can_msg can_msg; 22962306a36Sopenharmony_ci } msg; 23062306a36Sopenharmony_ci} __packed __aligned(0x4); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* IN Endpoint, inbound messages */ 23362306a36Sopenharmony_cistruct ucan_message_in { 23462306a36Sopenharmony_ci __le16 len; /* Length of the content include header */ 23562306a36Sopenharmony_ci u8 type; /* UCAN_IN_RX and friends */ 23662306a36Sopenharmony_ci u8 subtype; /* command sub type */ 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci union { 23962306a36Sopenharmony_ci /* CAN Frame received 24062306a36Sopenharmony_ci * (type == UCAN_IN_RX) 24162306a36Sopenharmony_ci * && ((msg.can_msg.id & CAN_RTR_FLAG) == 0) 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci struct ucan_can_msg can_msg; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* CAN transmission complete 24662306a36Sopenharmony_ci * (type == UCAN_IN_TX_COMPLETE) 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci DECLARE_FLEX_ARRAY(struct ucan_tx_complete_entry_t, 24962306a36Sopenharmony_ci can_tx_complete_msg); 25062306a36Sopenharmony_ci } __aligned(0x4) msg; 25162306a36Sopenharmony_ci} __packed __aligned(0x4); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* Macros to calculate message lengths */ 25462306a36Sopenharmony_ci#define UCAN_OUT_HDR_SIZE offsetof(struct ucan_message_out, msg) 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci#define UCAN_IN_HDR_SIZE offsetof(struct ucan_message_in, msg) 25762306a36Sopenharmony_ci#define UCAN_IN_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member)) 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistruct ucan_priv; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* Context Information for transmission URBs */ 26262306a36Sopenharmony_cistruct ucan_urb_context { 26362306a36Sopenharmony_ci struct ucan_priv *up; 26462306a36Sopenharmony_ci bool allocated; 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* Information reported by the USB device */ 26862306a36Sopenharmony_cistruct ucan_device_info { 26962306a36Sopenharmony_ci struct can_bittiming_const bittiming_const; 27062306a36Sopenharmony_ci u8 tx_fifo; 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/* Driver private data */ 27462306a36Sopenharmony_cistruct ucan_priv { 27562306a36Sopenharmony_ci /* must be the first member */ 27662306a36Sopenharmony_ci struct can_priv can; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* linux USB device structures */ 27962306a36Sopenharmony_ci struct usb_device *udev; 28062306a36Sopenharmony_ci struct net_device *netdev; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* lock for can->echo_skb (used around 28362306a36Sopenharmony_ci * can_put/get/free_echo_skb 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci spinlock_t echo_skb_lock; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* usb device information */ 28862306a36Sopenharmony_ci u8 intf_index; 28962306a36Sopenharmony_ci u8 in_ep_addr; 29062306a36Sopenharmony_ci u8 out_ep_addr; 29162306a36Sopenharmony_ci u16 in_ep_size; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* transmission and reception buffers */ 29462306a36Sopenharmony_ci struct usb_anchor rx_urbs; 29562306a36Sopenharmony_ci struct usb_anchor tx_urbs; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci union ucan_ctl_payload *ctl_msg_buffer; 29862306a36Sopenharmony_ci struct ucan_device_info device_info; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* transmission control information and locks */ 30162306a36Sopenharmony_ci spinlock_t context_lock; 30262306a36Sopenharmony_ci unsigned int available_tx_urbs; 30362306a36Sopenharmony_ci struct ucan_urb_context *context_array; 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic u8 ucan_can_cc_dlc2len(struct ucan_can_msg *msg, u16 len) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci if (le32_to_cpu(msg->id) & CAN_RTR_FLAG) 30962306a36Sopenharmony_ci return can_cc_dlc2len(msg->dlc); 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci return can_cc_dlc2len(len - (UCAN_IN_HDR_SIZE + sizeof(msg->id))); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void ucan_release_context_array(struct ucan_priv *up) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci if (!up->context_array) 31762306a36Sopenharmony_ci return; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* lock is not needed because, driver is currently opening or closing */ 32062306a36Sopenharmony_ci up->available_tx_urbs = 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci kfree(up->context_array); 32362306a36Sopenharmony_ci up->context_array = NULL; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int ucan_alloc_context_array(struct ucan_priv *up) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci int i; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* release contexts if any */ 33162306a36Sopenharmony_ci ucan_release_context_array(up); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci up->context_array = kcalloc(up->device_info.tx_fifo, 33462306a36Sopenharmony_ci sizeof(*up->context_array), 33562306a36Sopenharmony_ci GFP_KERNEL); 33662306a36Sopenharmony_ci if (!up->context_array) { 33762306a36Sopenharmony_ci netdev_err(up->netdev, 33862306a36Sopenharmony_ci "Not enough memory to allocate tx contexts\n"); 33962306a36Sopenharmony_ci return -ENOMEM; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (i = 0; i < up->device_info.tx_fifo; i++) { 34362306a36Sopenharmony_ci up->context_array[i].allocated = false; 34462306a36Sopenharmony_ci up->context_array[i].up = up; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* lock is not needed because, driver is currently opening */ 34862306a36Sopenharmony_ci up->available_tx_urbs = up->device_info.tx_fifo; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic struct ucan_urb_context *ucan_alloc_context(struct ucan_priv *up) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci int i; 35662306a36Sopenharmony_ci unsigned long flags; 35762306a36Sopenharmony_ci struct ucan_urb_context *ret = NULL; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (WARN_ON_ONCE(!up->context_array)) 36062306a36Sopenharmony_ci return NULL; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* execute context operation atomically */ 36362306a36Sopenharmony_ci spin_lock_irqsave(&up->context_lock, flags); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci for (i = 0; i < up->device_info.tx_fifo; i++) { 36662306a36Sopenharmony_ci if (!up->context_array[i].allocated) { 36762306a36Sopenharmony_ci /* update context */ 36862306a36Sopenharmony_ci ret = &up->context_array[i]; 36962306a36Sopenharmony_ci up->context_array[i].allocated = true; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* stop queue if necessary */ 37262306a36Sopenharmony_ci up->available_tx_urbs--; 37362306a36Sopenharmony_ci if (!up->available_tx_urbs) 37462306a36Sopenharmony_ci netif_stop_queue(up->netdev); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci spin_unlock_irqrestore(&up->context_lock, flags); 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic bool ucan_release_context(struct ucan_priv *up, 38562306a36Sopenharmony_ci struct ucan_urb_context *ctx) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci unsigned long flags; 38862306a36Sopenharmony_ci bool ret = false; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (WARN_ON_ONCE(!up->context_array)) 39162306a36Sopenharmony_ci return false; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* execute context operation atomically */ 39462306a36Sopenharmony_ci spin_lock_irqsave(&up->context_lock, flags); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* context was not allocated, maybe the device sent garbage */ 39762306a36Sopenharmony_ci if (ctx->allocated) { 39862306a36Sopenharmony_ci ctx->allocated = false; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* check if the queue needs to be woken */ 40162306a36Sopenharmony_ci if (!up->available_tx_urbs) 40262306a36Sopenharmony_ci netif_wake_queue(up->netdev); 40362306a36Sopenharmony_ci up->available_tx_urbs++; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ret = true; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci spin_unlock_irqrestore(&up->context_lock, flags); 40962306a36Sopenharmony_ci return ret; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int ucan_ctrl_command_out(struct ucan_priv *up, 41362306a36Sopenharmony_ci u8 cmd, u16 subcmd, u16 datalen) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci return usb_control_msg(up->udev, 41662306a36Sopenharmony_ci usb_sndctrlpipe(up->udev, 0), 41762306a36Sopenharmony_ci cmd, 41862306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | 41962306a36Sopenharmony_ci USB_RECIP_INTERFACE, 42062306a36Sopenharmony_ci subcmd, 42162306a36Sopenharmony_ci up->intf_index, 42262306a36Sopenharmony_ci up->ctl_msg_buffer, 42362306a36Sopenharmony_ci datalen, 42462306a36Sopenharmony_ci UCAN_USB_CTL_PIPE_TIMEOUT); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int ucan_device_request_in(struct ucan_priv *up, 42862306a36Sopenharmony_ci u8 cmd, u16 subcmd, u16 datalen) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci return usb_control_msg(up->udev, 43162306a36Sopenharmony_ci usb_rcvctrlpipe(up->udev, 0), 43262306a36Sopenharmony_ci cmd, 43362306a36Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 43462306a36Sopenharmony_ci subcmd, 43562306a36Sopenharmony_ci 0, 43662306a36Sopenharmony_ci up->ctl_msg_buffer, 43762306a36Sopenharmony_ci datalen, 43862306a36Sopenharmony_ci UCAN_USB_CTL_PIPE_TIMEOUT); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/* Parse the device information structure reported by the device and 44262306a36Sopenharmony_ci * setup private variables accordingly 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_cistatic void ucan_parse_device_info(struct ucan_priv *up, 44562306a36Sopenharmony_ci struct ucan_ctl_cmd_device_info *device_info) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct can_bittiming_const *bittiming = 44862306a36Sopenharmony_ci &up->device_info.bittiming_const; 44962306a36Sopenharmony_ci u16 ctrlmodes; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* store the data */ 45262306a36Sopenharmony_ci up->can.clock.freq = le32_to_cpu(device_info->freq); 45362306a36Sopenharmony_ci up->device_info.tx_fifo = device_info->tx_fifo; 45462306a36Sopenharmony_ci strcpy(bittiming->name, "ucan"); 45562306a36Sopenharmony_ci bittiming->tseg1_min = device_info->tseg1_min; 45662306a36Sopenharmony_ci bittiming->tseg1_max = device_info->tseg1_max; 45762306a36Sopenharmony_ci bittiming->tseg2_min = device_info->tseg2_min; 45862306a36Sopenharmony_ci bittiming->tseg2_max = device_info->tseg2_max; 45962306a36Sopenharmony_ci bittiming->sjw_max = device_info->sjw_max; 46062306a36Sopenharmony_ci bittiming->brp_min = le32_to_cpu(device_info->brp_min); 46162306a36Sopenharmony_ci bittiming->brp_max = le32_to_cpu(device_info->brp_max); 46262306a36Sopenharmony_ci bittiming->brp_inc = le16_to_cpu(device_info->brp_inc); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ctrlmodes = le16_to_cpu(device_info->ctrlmodes); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci up->can.ctrlmode_supported = 0; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (ctrlmodes & UCAN_MODE_LOOPBACK) 46962306a36Sopenharmony_ci up->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK; 47062306a36Sopenharmony_ci if (ctrlmodes & UCAN_MODE_SILENT) 47162306a36Sopenharmony_ci up->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; 47262306a36Sopenharmony_ci if (ctrlmodes & UCAN_MODE_3_SAMPLES) 47362306a36Sopenharmony_ci up->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; 47462306a36Sopenharmony_ci if (ctrlmodes & UCAN_MODE_ONE_SHOT) 47562306a36Sopenharmony_ci up->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; 47662306a36Sopenharmony_ci if (ctrlmodes & UCAN_MODE_BERR_REPORT) 47762306a36Sopenharmony_ci up->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/* Handle a CAN error frame that we have received from the device. 48162306a36Sopenharmony_ci * Returns true if the can state has changed. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_cistatic bool ucan_handle_error_frame(struct ucan_priv *up, 48462306a36Sopenharmony_ci struct ucan_message_in *m, 48562306a36Sopenharmony_ci canid_t canid) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci enum can_state new_state = up->can.state; 48862306a36Sopenharmony_ci struct net_device_stats *net_stats = &up->netdev->stats; 48962306a36Sopenharmony_ci struct can_device_stats *can_stats = &up->can.can_stats; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (canid & CAN_ERR_LOSTARB) 49262306a36Sopenharmony_ci can_stats->arbitration_lost++; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (canid & CAN_ERR_BUSERROR) 49562306a36Sopenharmony_ci can_stats->bus_error++; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (canid & CAN_ERR_ACK) 49862306a36Sopenharmony_ci net_stats->tx_errors++; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (canid & CAN_ERR_BUSOFF) 50162306a36Sopenharmony_ci new_state = CAN_STATE_BUS_OFF; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* controller problems, details in data[1] */ 50462306a36Sopenharmony_ci if (canid & CAN_ERR_CRTL) { 50562306a36Sopenharmony_ci u8 d1 = m->msg.can_msg.data[1]; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (d1 & CAN_ERR_CRTL_RX_OVERFLOW) 50862306a36Sopenharmony_ci net_stats->rx_over_errors++; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* controller state bits: if multiple are set the worst wins */ 51162306a36Sopenharmony_ci if (d1 & CAN_ERR_CRTL_ACTIVE) 51262306a36Sopenharmony_ci new_state = CAN_STATE_ERROR_ACTIVE; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (d1 & (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING)) 51562306a36Sopenharmony_ci new_state = CAN_STATE_ERROR_WARNING; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (d1 & (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE)) 51862306a36Sopenharmony_ci new_state = CAN_STATE_ERROR_PASSIVE; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* protocol error, details in data[2] */ 52262306a36Sopenharmony_ci if (canid & CAN_ERR_PROT) { 52362306a36Sopenharmony_ci u8 d2 = m->msg.can_msg.data[2]; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (d2 & CAN_ERR_PROT_TX) 52662306a36Sopenharmony_ci net_stats->tx_errors++; 52762306a36Sopenharmony_ci else 52862306a36Sopenharmony_ci net_stats->rx_errors++; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* no state change - we are done */ 53262306a36Sopenharmony_ci if (up->can.state == new_state) 53362306a36Sopenharmony_ci return false; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* we switched into a better state */ 53662306a36Sopenharmony_ci if (up->can.state > new_state) { 53762306a36Sopenharmony_ci up->can.state = new_state; 53862306a36Sopenharmony_ci return true; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* we switched into a worse state */ 54262306a36Sopenharmony_ci up->can.state = new_state; 54362306a36Sopenharmony_ci switch (new_state) { 54462306a36Sopenharmony_ci case CAN_STATE_BUS_OFF: 54562306a36Sopenharmony_ci can_stats->bus_off++; 54662306a36Sopenharmony_ci can_bus_off(up->netdev); 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci case CAN_STATE_ERROR_PASSIVE: 54962306a36Sopenharmony_ci can_stats->error_passive++; 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci case CAN_STATE_ERROR_WARNING: 55262306a36Sopenharmony_ci can_stats->error_warning++; 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci default: 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci return true; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/* Callback on reception of a can frame via the IN endpoint 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * This function allocates an skb and transferres it to the Linux 56362306a36Sopenharmony_ci * network stack 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_cistatic void ucan_rx_can_msg(struct ucan_priv *up, struct ucan_message_in *m) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci int len; 56862306a36Sopenharmony_ci canid_t canid; 56962306a36Sopenharmony_ci struct can_frame *cf; 57062306a36Sopenharmony_ci struct sk_buff *skb; 57162306a36Sopenharmony_ci struct net_device_stats *stats = &up->netdev->stats; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* get the contents of the length field */ 57462306a36Sopenharmony_ci len = le16_to_cpu(m->len); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* check sanity */ 57762306a36Sopenharmony_ci if (len < UCAN_IN_HDR_SIZE + sizeof(m->msg.can_msg.id)) { 57862306a36Sopenharmony_ci netdev_warn(up->netdev, "invalid input message len: %d\n", len); 57962306a36Sopenharmony_ci return; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* handle error frames */ 58362306a36Sopenharmony_ci canid = le32_to_cpu(m->msg.can_msg.id); 58462306a36Sopenharmony_ci if (canid & CAN_ERR_FLAG) { 58562306a36Sopenharmony_ci bool busstate_changed = ucan_handle_error_frame(up, m, canid); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* if berr-reporting is off only state changes get through */ 58862306a36Sopenharmony_ci if (!(up->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && 58962306a36Sopenharmony_ci !busstate_changed) 59062306a36Sopenharmony_ci return; 59162306a36Sopenharmony_ci } else { 59262306a36Sopenharmony_ci canid_t canid_mask; 59362306a36Sopenharmony_ci /* compute the mask for canid */ 59462306a36Sopenharmony_ci canid_mask = CAN_RTR_FLAG; 59562306a36Sopenharmony_ci if (canid & CAN_EFF_FLAG) 59662306a36Sopenharmony_ci canid_mask |= CAN_EFF_MASK | CAN_EFF_FLAG; 59762306a36Sopenharmony_ci else 59862306a36Sopenharmony_ci canid_mask |= CAN_SFF_MASK; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (canid & ~canid_mask) 60162306a36Sopenharmony_ci netdev_warn(up->netdev, 60262306a36Sopenharmony_ci "unexpected bits set (canid %x, mask %x)", 60362306a36Sopenharmony_ci canid, canid_mask); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci canid &= canid_mask; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* allocate skb */ 60962306a36Sopenharmony_ci skb = alloc_can_skb(up->netdev, &cf); 61062306a36Sopenharmony_ci if (!skb) 61162306a36Sopenharmony_ci return; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* fill the can frame */ 61462306a36Sopenharmony_ci cf->can_id = canid; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* compute DLC taking RTR_FLAG into account */ 61762306a36Sopenharmony_ci cf->len = ucan_can_cc_dlc2len(&m->msg.can_msg, len); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* copy the payload of non RTR frames */ 62062306a36Sopenharmony_ci if (!(cf->can_id & CAN_RTR_FLAG) || (cf->can_id & CAN_ERR_FLAG)) 62162306a36Sopenharmony_ci memcpy(cf->data, m->msg.can_msg.data, cf->len); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* don't count error frames as real packets */ 62462306a36Sopenharmony_ci if (!(cf->can_id & CAN_ERR_FLAG)) { 62562306a36Sopenharmony_ci stats->rx_packets++; 62662306a36Sopenharmony_ci if (!(cf->can_id & CAN_RTR_FLAG)) 62762306a36Sopenharmony_ci stats->rx_bytes += cf->len; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* pass it to Linux */ 63162306a36Sopenharmony_ci netif_rx(skb); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/* callback indicating completed transmission */ 63562306a36Sopenharmony_cistatic void ucan_tx_complete_msg(struct ucan_priv *up, 63662306a36Sopenharmony_ci struct ucan_message_in *m) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci unsigned long flags; 63962306a36Sopenharmony_ci u16 count, i; 64062306a36Sopenharmony_ci u8 echo_index; 64162306a36Sopenharmony_ci u16 len = le16_to_cpu(m->len); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci struct ucan_urb_context *context; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (len < UCAN_IN_HDR_SIZE || (len % 2 != 0)) { 64662306a36Sopenharmony_ci netdev_err(up->netdev, "invalid tx complete length\n"); 64762306a36Sopenharmony_ci return; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci count = (len - UCAN_IN_HDR_SIZE) / 2; 65162306a36Sopenharmony_ci for (i = 0; i < count; i++) { 65262306a36Sopenharmony_ci /* we did not submit such echo ids */ 65362306a36Sopenharmony_ci echo_index = m->msg.can_tx_complete_msg[i].echo_index; 65462306a36Sopenharmony_ci if (echo_index >= up->device_info.tx_fifo) { 65562306a36Sopenharmony_ci up->netdev->stats.tx_errors++; 65662306a36Sopenharmony_ci netdev_err(up->netdev, 65762306a36Sopenharmony_ci "invalid echo_index %d received\n", 65862306a36Sopenharmony_ci echo_index); 65962306a36Sopenharmony_ci continue; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* gather information from the context */ 66362306a36Sopenharmony_ci context = &up->context_array[echo_index]; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* Release context and restart queue if necessary. 66662306a36Sopenharmony_ci * Also check if the context was allocated 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci if (!ucan_release_context(up, context)) 66962306a36Sopenharmony_ci continue; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci spin_lock_irqsave(&up->echo_skb_lock, flags); 67262306a36Sopenharmony_ci if (m->msg.can_tx_complete_msg[i].flags & 67362306a36Sopenharmony_ci UCAN_TX_COMPLETE_SUCCESS) { 67462306a36Sopenharmony_ci /* update statistics */ 67562306a36Sopenharmony_ci up->netdev->stats.tx_packets++; 67662306a36Sopenharmony_ci up->netdev->stats.tx_bytes += 67762306a36Sopenharmony_ci can_get_echo_skb(up->netdev, echo_index, NULL); 67862306a36Sopenharmony_ci } else { 67962306a36Sopenharmony_ci up->netdev->stats.tx_dropped++; 68062306a36Sopenharmony_ci can_free_echo_skb(up->netdev, echo_index, NULL); 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci spin_unlock_irqrestore(&up->echo_skb_lock, flags); 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/* callback on reception of a USB message */ 68762306a36Sopenharmony_cistatic void ucan_read_bulk_callback(struct urb *urb) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci int ret; 69062306a36Sopenharmony_ci int pos; 69162306a36Sopenharmony_ci struct ucan_priv *up = urb->context; 69262306a36Sopenharmony_ci struct net_device *netdev = up->netdev; 69362306a36Sopenharmony_ci struct ucan_message_in *m; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* the device is not up and the driver should not receive any 69662306a36Sopenharmony_ci * data on the bulk in pipe 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci if (WARN_ON(!up->context_array)) { 69962306a36Sopenharmony_ci usb_free_coherent(up->udev, 70062306a36Sopenharmony_ci up->in_ep_size, 70162306a36Sopenharmony_ci urb->transfer_buffer, 70262306a36Sopenharmony_ci urb->transfer_dma); 70362306a36Sopenharmony_ci return; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* check URB status */ 70762306a36Sopenharmony_ci switch (urb->status) { 70862306a36Sopenharmony_ci case 0: 70962306a36Sopenharmony_ci break; 71062306a36Sopenharmony_ci case -ENOENT: 71162306a36Sopenharmony_ci case -EPIPE: 71262306a36Sopenharmony_ci case -EPROTO: 71362306a36Sopenharmony_ci case -ESHUTDOWN: 71462306a36Sopenharmony_ci case -ETIME: 71562306a36Sopenharmony_ci /* urb is not resubmitted -> free dma data */ 71662306a36Sopenharmony_ci usb_free_coherent(up->udev, 71762306a36Sopenharmony_ci up->in_ep_size, 71862306a36Sopenharmony_ci urb->transfer_buffer, 71962306a36Sopenharmony_ci urb->transfer_dma); 72062306a36Sopenharmony_ci netdev_dbg(up->netdev, "not resubmitting urb; status: %d\n", 72162306a36Sopenharmony_ci urb->status); 72262306a36Sopenharmony_ci return; 72362306a36Sopenharmony_ci default: 72462306a36Sopenharmony_ci goto resubmit; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* sanity check */ 72862306a36Sopenharmony_ci if (!netif_device_present(netdev)) 72962306a36Sopenharmony_ci return; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* iterate over input */ 73262306a36Sopenharmony_ci pos = 0; 73362306a36Sopenharmony_ci while (pos < urb->actual_length) { 73462306a36Sopenharmony_ci int len; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* check sanity (length of header) */ 73762306a36Sopenharmony_ci if ((urb->actual_length - pos) < UCAN_IN_HDR_SIZE) { 73862306a36Sopenharmony_ci netdev_warn(up->netdev, 73962306a36Sopenharmony_ci "invalid message (short; no hdr; l:%d)\n", 74062306a36Sopenharmony_ci urb->actual_length); 74162306a36Sopenharmony_ci goto resubmit; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* setup the message address */ 74562306a36Sopenharmony_ci m = (struct ucan_message_in *) 74662306a36Sopenharmony_ci ((u8 *)urb->transfer_buffer + pos); 74762306a36Sopenharmony_ci len = le16_to_cpu(m->len); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* check sanity (length of content) */ 75062306a36Sopenharmony_ci if (urb->actual_length - pos < len) { 75162306a36Sopenharmony_ci netdev_warn(up->netdev, 75262306a36Sopenharmony_ci "invalid message (short; no data; l:%d)\n", 75362306a36Sopenharmony_ci urb->actual_length); 75462306a36Sopenharmony_ci print_hex_dump(KERN_WARNING, 75562306a36Sopenharmony_ci "raw data: ", 75662306a36Sopenharmony_ci DUMP_PREFIX_ADDRESS, 75762306a36Sopenharmony_ci 16, 75862306a36Sopenharmony_ci 1, 75962306a36Sopenharmony_ci urb->transfer_buffer, 76062306a36Sopenharmony_ci urb->actual_length, 76162306a36Sopenharmony_ci true); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci goto resubmit; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci switch (m->type) { 76762306a36Sopenharmony_ci case UCAN_IN_RX: 76862306a36Sopenharmony_ci ucan_rx_can_msg(up, m); 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci case UCAN_IN_TX_COMPLETE: 77162306a36Sopenharmony_ci ucan_tx_complete_msg(up, m); 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci default: 77462306a36Sopenharmony_ci netdev_warn(up->netdev, 77562306a36Sopenharmony_ci "invalid message (type; t:%d)\n", 77662306a36Sopenharmony_ci m->type); 77762306a36Sopenharmony_ci break; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* proceed to next message */ 78162306a36Sopenharmony_ci pos += len; 78262306a36Sopenharmony_ci /* align to 4 byte boundary */ 78362306a36Sopenharmony_ci pos = round_up(pos, 4); 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ciresubmit: 78762306a36Sopenharmony_ci /* resubmit urb when done */ 78862306a36Sopenharmony_ci usb_fill_bulk_urb(urb, up->udev, 78962306a36Sopenharmony_ci usb_rcvbulkpipe(up->udev, 79062306a36Sopenharmony_ci up->in_ep_addr), 79162306a36Sopenharmony_ci urb->transfer_buffer, 79262306a36Sopenharmony_ci up->in_ep_size, 79362306a36Sopenharmony_ci ucan_read_bulk_callback, 79462306a36Sopenharmony_ci up); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci usb_anchor_urb(urb, &up->rx_urbs); 79762306a36Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (ret < 0) { 80062306a36Sopenharmony_ci netdev_err(up->netdev, 80162306a36Sopenharmony_ci "failed resubmitting read bulk urb: %d\n", 80262306a36Sopenharmony_ci ret); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci usb_unanchor_urb(urb); 80562306a36Sopenharmony_ci usb_free_coherent(up->udev, 80662306a36Sopenharmony_ci up->in_ep_size, 80762306a36Sopenharmony_ci urb->transfer_buffer, 80862306a36Sopenharmony_ci urb->transfer_dma); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (ret == -ENODEV) 81162306a36Sopenharmony_ci netif_device_detach(netdev); 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci/* callback after transmission of a USB message */ 81662306a36Sopenharmony_cistatic void ucan_write_bulk_callback(struct urb *urb) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci unsigned long flags; 81962306a36Sopenharmony_ci struct ucan_priv *up; 82062306a36Sopenharmony_ci struct ucan_urb_context *context = urb->context; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* get the urb context */ 82362306a36Sopenharmony_ci if (WARN_ON_ONCE(!context)) 82462306a36Sopenharmony_ci return; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* free up our allocated buffer */ 82762306a36Sopenharmony_ci usb_free_coherent(urb->dev, 82862306a36Sopenharmony_ci sizeof(struct ucan_message_out), 82962306a36Sopenharmony_ci urb->transfer_buffer, 83062306a36Sopenharmony_ci urb->transfer_dma); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci up = context->up; 83362306a36Sopenharmony_ci if (WARN_ON_ONCE(!up)) 83462306a36Sopenharmony_ci return; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* sanity check */ 83762306a36Sopenharmony_ci if (!netif_device_present(up->netdev)) 83862306a36Sopenharmony_ci return; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* transmission failed (USB - the device will not send a TX complete) */ 84162306a36Sopenharmony_ci if (urb->status) { 84262306a36Sopenharmony_ci netdev_warn(up->netdev, 84362306a36Sopenharmony_ci "failed to transmit USB message to device: %d\n", 84462306a36Sopenharmony_ci urb->status); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* update counters an cleanup */ 84762306a36Sopenharmony_ci spin_lock_irqsave(&up->echo_skb_lock, flags); 84862306a36Sopenharmony_ci can_free_echo_skb(up->netdev, context - up->context_array, NULL); 84962306a36Sopenharmony_ci spin_unlock_irqrestore(&up->echo_skb_lock, flags); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci up->netdev->stats.tx_dropped++; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* release context and restart the queue if necessary */ 85462306a36Sopenharmony_ci if (!ucan_release_context(up, context)) 85562306a36Sopenharmony_ci netdev_err(up->netdev, 85662306a36Sopenharmony_ci "urb failed, failed to release context\n"); 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void ucan_cleanup_rx_urbs(struct ucan_priv *up, struct urb **urbs) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci int i; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci for (i = 0; i < UCAN_MAX_RX_URBS; i++) { 86562306a36Sopenharmony_ci if (urbs[i]) { 86662306a36Sopenharmony_ci usb_unanchor_urb(urbs[i]); 86762306a36Sopenharmony_ci usb_free_coherent(up->udev, 86862306a36Sopenharmony_ci up->in_ep_size, 86962306a36Sopenharmony_ci urbs[i]->transfer_buffer, 87062306a36Sopenharmony_ci urbs[i]->transfer_dma); 87162306a36Sopenharmony_ci usb_free_urb(urbs[i]); 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS); 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic int ucan_prepare_and_anchor_rx_urbs(struct ucan_priv *up, 87962306a36Sopenharmony_ci struct urb **urbs) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci int i; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci memset(urbs, 0, sizeof(*urbs) * UCAN_MAX_RX_URBS); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci for (i = 0; i < UCAN_MAX_RX_URBS; i++) { 88662306a36Sopenharmony_ci void *buf; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci urbs[i] = usb_alloc_urb(0, GFP_KERNEL); 88962306a36Sopenharmony_ci if (!urbs[i]) 89062306a36Sopenharmony_ci goto err; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci buf = usb_alloc_coherent(up->udev, 89362306a36Sopenharmony_ci up->in_ep_size, 89462306a36Sopenharmony_ci GFP_KERNEL, &urbs[i]->transfer_dma); 89562306a36Sopenharmony_ci if (!buf) { 89662306a36Sopenharmony_ci /* cleanup this urb */ 89762306a36Sopenharmony_ci usb_free_urb(urbs[i]); 89862306a36Sopenharmony_ci urbs[i] = NULL; 89962306a36Sopenharmony_ci goto err; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci usb_fill_bulk_urb(urbs[i], up->udev, 90362306a36Sopenharmony_ci usb_rcvbulkpipe(up->udev, 90462306a36Sopenharmony_ci up->in_ep_addr), 90562306a36Sopenharmony_ci buf, 90662306a36Sopenharmony_ci up->in_ep_size, 90762306a36Sopenharmony_ci ucan_read_bulk_callback, 90862306a36Sopenharmony_ci up); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci urbs[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci usb_anchor_urb(urbs[i], &up->rx_urbs); 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci return 0; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cierr: 91762306a36Sopenharmony_ci /* cleanup other unsubmitted urbs */ 91862306a36Sopenharmony_ci ucan_cleanup_rx_urbs(up, urbs); 91962306a36Sopenharmony_ci return -ENOMEM; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci/* Submits rx urbs with the semantic: Either submit all, or cleanup 92362306a36Sopenharmony_ci * everything. I case of errors submitted urbs are killed and all urbs in 92462306a36Sopenharmony_ci * the array are freed. I case of no errors every entry in the urb 92562306a36Sopenharmony_ci * array is set to NULL. 92662306a36Sopenharmony_ci */ 92762306a36Sopenharmony_cistatic int ucan_submit_rx_urbs(struct ucan_priv *up, struct urb **urbs) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci int i, ret; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* Iterate over all urbs to submit. On success remove the urb 93262306a36Sopenharmony_ci * from the list. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci for (i = 0; i < UCAN_MAX_RX_URBS; i++) { 93562306a36Sopenharmony_ci ret = usb_submit_urb(urbs[i], GFP_KERNEL); 93662306a36Sopenharmony_ci if (ret) { 93762306a36Sopenharmony_ci netdev_err(up->netdev, 93862306a36Sopenharmony_ci "could not submit urb; code: %d\n", 93962306a36Sopenharmony_ci ret); 94062306a36Sopenharmony_ci goto err; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* Anchor URB and drop reference, USB core will take 94462306a36Sopenharmony_ci * care of freeing it 94562306a36Sopenharmony_ci */ 94662306a36Sopenharmony_ci usb_free_urb(urbs[i]); 94762306a36Sopenharmony_ci urbs[i] = NULL; 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci return 0; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cierr: 95262306a36Sopenharmony_ci /* Cleanup unsubmitted urbs */ 95362306a36Sopenharmony_ci ucan_cleanup_rx_urbs(up, urbs); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* Kill urbs that are already submitted */ 95662306a36Sopenharmony_ci usb_kill_anchored_urbs(&up->rx_urbs); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return ret; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* Open the network device */ 96262306a36Sopenharmony_cistatic int ucan_open(struct net_device *netdev) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci int ret, ret_cleanup; 96562306a36Sopenharmony_ci u16 ctrlmode; 96662306a36Sopenharmony_ci struct urb *urbs[UCAN_MAX_RX_URBS]; 96762306a36Sopenharmony_ci struct ucan_priv *up = netdev_priv(netdev); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci ret = ucan_alloc_context_array(up); 97062306a36Sopenharmony_ci if (ret) 97162306a36Sopenharmony_ci return ret; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* Allocate and prepare IN URBS - allocated and anchored 97462306a36Sopenharmony_ci * urbs are stored in urbs[] for clean 97562306a36Sopenharmony_ci */ 97662306a36Sopenharmony_ci ret = ucan_prepare_and_anchor_rx_urbs(up, urbs); 97762306a36Sopenharmony_ci if (ret) 97862306a36Sopenharmony_ci goto err_contexts; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* Check the control mode */ 98162306a36Sopenharmony_ci ctrlmode = 0; 98262306a36Sopenharmony_ci if (up->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) 98362306a36Sopenharmony_ci ctrlmode |= UCAN_MODE_LOOPBACK; 98462306a36Sopenharmony_ci if (up->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) 98562306a36Sopenharmony_ci ctrlmode |= UCAN_MODE_SILENT; 98662306a36Sopenharmony_ci if (up->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) 98762306a36Sopenharmony_ci ctrlmode |= UCAN_MODE_3_SAMPLES; 98862306a36Sopenharmony_ci if (up->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) 98962306a36Sopenharmony_ci ctrlmode |= UCAN_MODE_ONE_SHOT; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* Enable this in any case - filtering is down within the 99262306a36Sopenharmony_ci * receive path 99362306a36Sopenharmony_ci */ 99462306a36Sopenharmony_ci ctrlmode |= UCAN_MODE_BERR_REPORT; 99562306a36Sopenharmony_ci up->ctl_msg_buffer->cmd_start.mode = cpu_to_le16(ctrlmode); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* Driver is ready to receive data - start the USB device */ 99862306a36Sopenharmony_ci ret = ucan_ctrl_command_out(up, UCAN_COMMAND_START, 0, 2); 99962306a36Sopenharmony_ci if (ret < 0) { 100062306a36Sopenharmony_ci netdev_err(up->netdev, 100162306a36Sopenharmony_ci "could not start device, code: %d\n", 100262306a36Sopenharmony_ci ret); 100362306a36Sopenharmony_ci goto err_reset; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* Call CAN layer open */ 100762306a36Sopenharmony_ci ret = open_candev(netdev); 100862306a36Sopenharmony_ci if (ret) 100962306a36Sopenharmony_ci goto err_stop; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* Driver is ready to receive data. Submit RX URBS */ 101262306a36Sopenharmony_ci ret = ucan_submit_rx_urbs(up, urbs); 101362306a36Sopenharmony_ci if (ret) 101462306a36Sopenharmony_ci goto err_stop; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci up->can.state = CAN_STATE_ERROR_ACTIVE; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci /* Start the network queue */ 101962306a36Sopenharmony_ci netif_start_queue(netdev); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci return 0; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cierr_stop: 102462306a36Sopenharmony_ci /* The device have started already stop it */ 102562306a36Sopenharmony_ci ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_STOP, 0, 0); 102662306a36Sopenharmony_ci if (ret_cleanup < 0) 102762306a36Sopenharmony_ci netdev_err(up->netdev, 102862306a36Sopenharmony_ci "could not stop device, code: %d\n", 102962306a36Sopenharmony_ci ret_cleanup); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cierr_reset: 103262306a36Sopenharmony_ci /* The device might have received data, reset it for 103362306a36Sopenharmony_ci * consistent state 103462306a36Sopenharmony_ci */ 103562306a36Sopenharmony_ci ret_cleanup = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); 103662306a36Sopenharmony_ci if (ret_cleanup < 0) 103762306a36Sopenharmony_ci netdev_err(up->netdev, 103862306a36Sopenharmony_ci "could not reset device, code: %d\n", 103962306a36Sopenharmony_ci ret_cleanup); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* clean up unsubmitted urbs */ 104262306a36Sopenharmony_ci ucan_cleanup_rx_urbs(up, urbs); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cierr_contexts: 104562306a36Sopenharmony_ci ucan_release_context_array(up); 104662306a36Sopenharmony_ci return ret; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic struct urb *ucan_prepare_tx_urb(struct ucan_priv *up, 105062306a36Sopenharmony_ci struct ucan_urb_context *context, 105162306a36Sopenharmony_ci struct can_frame *cf, 105262306a36Sopenharmony_ci u8 echo_index) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci int mlen; 105562306a36Sopenharmony_ci struct urb *urb; 105662306a36Sopenharmony_ci struct ucan_message_out *m; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci /* create a URB, and a buffer for it, and copy the data to the URB */ 105962306a36Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 106062306a36Sopenharmony_ci if (!urb) { 106162306a36Sopenharmony_ci netdev_err(up->netdev, "no memory left for URBs\n"); 106262306a36Sopenharmony_ci return NULL; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci m = usb_alloc_coherent(up->udev, 106662306a36Sopenharmony_ci sizeof(struct ucan_message_out), 106762306a36Sopenharmony_ci GFP_ATOMIC, 106862306a36Sopenharmony_ci &urb->transfer_dma); 106962306a36Sopenharmony_ci if (!m) { 107062306a36Sopenharmony_ci netdev_err(up->netdev, "no memory left for USB buffer\n"); 107162306a36Sopenharmony_ci usb_free_urb(urb); 107262306a36Sopenharmony_ci return NULL; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* build the USB message */ 107662306a36Sopenharmony_ci m->type = UCAN_OUT_TX; 107762306a36Sopenharmony_ci m->msg.can_msg.id = cpu_to_le32(cf->can_id); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (cf->can_id & CAN_RTR_FLAG) { 108062306a36Sopenharmony_ci mlen = UCAN_OUT_HDR_SIZE + 108162306a36Sopenharmony_ci offsetof(struct ucan_can_msg, dlc) + 108262306a36Sopenharmony_ci sizeof(m->msg.can_msg.dlc); 108362306a36Sopenharmony_ci m->msg.can_msg.dlc = cf->len; 108462306a36Sopenharmony_ci } else { 108562306a36Sopenharmony_ci mlen = UCAN_OUT_HDR_SIZE + 108662306a36Sopenharmony_ci sizeof(m->msg.can_msg.id) + cf->len; 108762306a36Sopenharmony_ci memcpy(m->msg.can_msg.data, cf->data, cf->len); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci m->len = cpu_to_le16(mlen); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci m->subtype = echo_index; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* build the urb */ 109462306a36Sopenharmony_ci usb_fill_bulk_urb(urb, up->udev, 109562306a36Sopenharmony_ci usb_sndbulkpipe(up->udev, 109662306a36Sopenharmony_ci up->out_ep_addr), 109762306a36Sopenharmony_ci m, mlen, ucan_write_bulk_callback, context); 109862306a36Sopenharmony_ci urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci return urb; 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic void ucan_clean_up_tx_urb(struct ucan_priv *up, struct urb *urb) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci usb_free_coherent(up->udev, sizeof(struct ucan_message_out), 110662306a36Sopenharmony_ci urb->transfer_buffer, urb->transfer_dma); 110762306a36Sopenharmony_ci usb_free_urb(urb); 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci/* callback when Linux needs to send a can frame */ 111162306a36Sopenharmony_cistatic netdev_tx_t ucan_start_xmit(struct sk_buff *skb, 111262306a36Sopenharmony_ci struct net_device *netdev) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci unsigned long flags; 111562306a36Sopenharmony_ci int ret; 111662306a36Sopenharmony_ci u8 echo_index; 111762306a36Sopenharmony_ci struct urb *urb; 111862306a36Sopenharmony_ci struct ucan_urb_context *context; 111962306a36Sopenharmony_ci struct ucan_priv *up = netdev_priv(netdev); 112062306a36Sopenharmony_ci struct can_frame *cf = (struct can_frame *)skb->data; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* check skb */ 112362306a36Sopenharmony_ci if (can_dev_dropped_skb(netdev, skb)) 112462306a36Sopenharmony_ci return NETDEV_TX_OK; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* allocate a context and slow down tx path, if fifo state is low */ 112762306a36Sopenharmony_ci context = ucan_alloc_context(up); 112862306a36Sopenharmony_ci echo_index = context - up->context_array; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (WARN_ON_ONCE(!context)) 113162306a36Sopenharmony_ci return NETDEV_TX_BUSY; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci /* prepare urb for transmission */ 113462306a36Sopenharmony_ci urb = ucan_prepare_tx_urb(up, context, cf, echo_index); 113562306a36Sopenharmony_ci if (!urb) 113662306a36Sopenharmony_ci goto drop; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* put the skb on can loopback stack */ 113962306a36Sopenharmony_ci spin_lock_irqsave(&up->echo_skb_lock, flags); 114062306a36Sopenharmony_ci can_put_echo_skb(skb, up->netdev, echo_index, 0); 114162306a36Sopenharmony_ci spin_unlock_irqrestore(&up->echo_skb_lock, flags); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* transmit it */ 114462306a36Sopenharmony_ci usb_anchor_urb(urb, &up->tx_urbs); 114562306a36Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* cleanup urb */ 114862306a36Sopenharmony_ci if (ret) { 114962306a36Sopenharmony_ci /* on error, clean up */ 115062306a36Sopenharmony_ci usb_unanchor_urb(urb); 115162306a36Sopenharmony_ci ucan_clean_up_tx_urb(up, urb); 115262306a36Sopenharmony_ci if (!ucan_release_context(up, context)) 115362306a36Sopenharmony_ci netdev_err(up->netdev, 115462306a36Sopenharmony_ci "xmit err: failed to release context\n"); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci /* remove the skb from the echo stack - this also 115762306a36Sopenharmony_ci * frees the skb 115862306a36Sopenharmony_ci */ 115962306a36Sopenharmony_ci spin_lock_irqsave(&up->echo_skb_lock, flags); 116062306a36Sopenharmony_ci can_free_echo_skb(up->netdev, echo_index, NULL); 116162306a36Sopenharmony_ci spin_unlock_irqrestore(&up->echo_skb_lock, flags); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (ret == -ENODEV) { 116462306a36Sopenharmony_ci netif_device_detach(up->netdev); 116562306a36Sopenharmony_ci } else { 116662306a36Sopenharmony_ci netdev_warn(up->netdev, 116762306a36Sopenharmony_ci "xmit err: failed to submit urb %d\n", 116862306a36Sopenharmony_ci ret); 116962306a36Sopenharmony_ci up->netdev->stats.tx_dropped++; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci return NETDEV_TX_OK; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci netif_trans_update(netdev); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci /* release ref, as we do not need the urb anymore */ 117762306a36Sopenharmony_ci usb_free_urb(urb); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci return NETDEV_TX_OK; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_cidrop: 118262306a36Sopenharmony_ci if (!ucan_release_context(up, context)) 118362306a36Sopenharmony_ci netdev_err(up->netdev, 118462306a36Sopenharmony_ci "xmit drop: failed to release context\n"); 118562306a36Sopenharmony_ci dev_kfree_skb(skb); 118662306a36Sopenharmony_ci up->netdev->stats.tx_dropped++; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci return NETDEV_TX_OK; 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci/* Device goes down 119262306a36Sopenharmony_ci * 119362306a36Sopenharmony_ci * Clean up used resources 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_cistatic int ucan_close(struct net_device *netdev) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci int ret; 119862306a36Sopenharmony_ci struct ucan_priv *up = netdev_priv(netdev); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci up->can.state = CAN_STATE_STOPPED; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* stop sending data */ 120362306a36Sopenharmony_ci usb_kill_anchored_urbs(&up->tx_urbs); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* stop receiving data */ 120662306a36Sopenharmony_ci usb_kill_anchored_urbs(&up->rx_urbs); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci /* stop and reset can device */ 120962306a36Sopenharmony_ci ret = ucan_ctrl_command_out(up, UCAN_COMMAND_STOP, 0, 0); 121062306a36Sopenharmony_ci if (ret < 0) 121162306a36Sopenharmony_ci netdev_err(up->netdev, 121262306a36Sopenharmony_ci "could not stop device, code: %d\n", 121362306a36Sopenharmony_ci ret); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); 121662306a36Sopenharmony_ci if (ret < 0) 121762306a36Sopenharmony_ci netdev_err(up->netdev, 121862306a36Sopenharmony_ci "could not reset device, code: %d\n", 121962306a36Sopenharmony_ci ret); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci netif_stop_queue(netdev); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci ucan_release_context_array(up); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci close_candev(up->netdev); 122662306a36Sopenharmony_ci return 0; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci/* CAN driver callbacks */ 123062306a36Sopenharmony_cistatic const struct net_device_ops ucan_netdev_ops = { 123162306a36Sopenharmony_ci .ndo_open = ucan_open, 123262306a36Sopenharmony_ci .ndo_stop = ucan_close, 123362306a36Sopenharmony_ci .ndo_start_xmit = ucan_start_xmit, 123462306a36Sopenharmony_ci .ndo_change_mtu = can_change_mtu, 123562306a36Sopenharmony_ci}; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic const struct ethtool_ops ucan_ethtool_ops = { 123862306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 123962306a36Sopenharmony_ci}; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci/* Request to set bittiming 124262306a36Sopenharmony_ci * 124362306a36Sopenharmony_ci * This function generates an USB set bittiming message and transmits 124462306a36Sopenharmony_ci * it to the device 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_cistatic int ucan_set_bittiming(struct net_device *netdev) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci int ret; 124962306a36Sopenharmony_ci struct ucan_priv *up = netdev_priv(netdev); 125062306a36Sopenharmony_ci struct ucan_ctl_cmd_set_bittiming *cmd_set_bittiming; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci cmd_set_bittiming = &up->ctl_msg_buffer->cmd_set_bittiming; 125362306a36Sopenharmony_ci cmd_set_bittiming->tq = cpu_to_le32(up->can.bittiming.tq); 125462306a36Sopenharmony_ci cmd_set_bittiming->brp = cpu_to_le16(up->can.bittiming.brp); 125562306a36Sopenharmony_ci cmd_set_bittiming->sample_point = 125662306a36Sopenharmony_ci cpu_to_le16(up->can.bittiming.sample_point); 125762306a36Sopenharmony_ci cmd_set_bittiming->prop_seg = up->can.bittiming.prop_seg; 125862306a36Sopenharmony_ci cmd_set_bittiming->phase_seg1 = up->can.bittiming.phase_seg1; 125962306a36Sopenharmony_ci cmd_set_bittiming->phase_seg2 = up->can.bittiming.phase_seg2; 126062306a36Sopenharmony_ci cmd_set_bittiming->sjw = up->can.bittiming.sjw; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci ret = ucan_ctrl_command_out(up, UCAN_COMMAND_SET_BITTIMING, 0, 126362306a36Sopenharmony_ci sizeof(*cmd_set_bittiming)); 126462306a36Sopenharmony_ci return (ret < 0) ? ret : 0; 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci/* Restart the device to get it out of BUS-OFF state. 126862306a36Sopenharmony_ci * Called when the user runs "ip link set can1 type can restart". 126962306a36Sopenharmony_ci */ 127062306a36Sopenharmony_cistatic int ucan_set_mode(struct net_device *netdev, enum can_mode mode) 127162306a36Sopenharmony_ci{ 127262306a36Sopenharmony_ci int ret; 127362306a36Sopenharmony_ci unsigned long flags; 127462306a36Sopenharmony_ci struct ucan_priv *up = netdev_priv(netdev); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci switch (mode) { 127762306a36Sopenharmony_ci case CAN_MODE_START: 127862306a36Sopenharmony_ci netdev_dbg(up->netdev, "restarting device\n"); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESTART, 0, 0); 128162306a36Sopenharmony_ci up->can.state = CAN_STATE_ERROR_ACTIVE; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci /* check if queue can be restarted, 128462306a36Sopenharmony_ci * up->available_tx_urbs must be protected by the 128562306a36Sopenharmony_ci * lock 128662306a36Sopenharmony_ci */ 128762306a36Sopenharmony_ci spin_lock_irqsave(&up->context_lock, flags); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (up->available_tx_urbs > 0) 129062306a36Sopenharmony_ci netif_wake_queue(up->netdev); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci spin_unlock_irqrestore(&up->context_lock, flags); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci return ret; 129562306a36Sopenharmony_ci default: 129662306a36Sopenharmony_ci return -EOPNOTSUPP; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci/* Probe the device, reset it and gather general device information */ 130162306a36Sopenharmony_cistatic int ucan_probe(struct usb_interface *intf, 130262306a36Sopenharmony_ci const struct usb_device_id *id) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci int ret; 130562306a36Sopenharmony_ci int i; 130662306a36Sopenharmony_ci u32 protocol_version; 130762306a36Sopenharmony_ci struct usb_device *udev; 130862306a36Sopenharmony_ci struct net_device *netdev; 130962306a36Sopenharmony_ci struct usb_host_interface *iface_desc; 131062306a36Sopenharmony_ci struct ucan_priv *up; 131162306a36Sopenharmony_ci struct usb_endpoint_descriptor *ep; 131262306a36Sopenharmony_ci u16 in_ep_size; 131362306a36Sopenharmony_ci u16 out_ep_size; 131462306a36Sopenharmony_ci u8 in_ep_addr; 131562306a36Sopenharmony_ci u8 out_ep_addr; 131662306a36Sopenharmony_ci union ucan_ctl_payload *ctl_msg_buffer; 131762306a36Sopenharmony_ci char firmware_str[sizeof(union ucan_ctl_payload) + 1]; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci udev = interface_to_usbdev(intf); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* Stage 1 - Interface Parsing 132262306a36Sopenharmony_ci * --------------------------- 132362306a36Sopenharmony_ci * 132462306a36Sopenharmony_ci * Identifie the device USB interface descriptor and its 132562306a36Sopenharmony_ci * endpoints. Probing is aborted on errors. 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci /* check if the interface is sane */ 132962306a36Sopenharmony_ci iface_desc = intf->cur_altsetting; 133062306a36Sopenharmony_ci if (!iface_desc) 133162306a36Sopenharmony_ci return -ENODEV; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci dev_info(&udev->dev, 133462306a36Sopenharmony_ci "%s: probing device on interface #%d\n", 133562306a36Sopenharmony_ci UCAN_DRIVER_NAME, 133662306a36Sopenharmony_ci iface_desc->desc.bInterfaceNumber); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* interface sanity check */ 133962306a36Sopenharmony_ci if (iface_desc->desc.bNumEndpoints != 2) { 134062306a36Sopenharmony_ci dev_err(&udev->dev, 134162306a36Sopenharmony_ci "%s: invalid EP count (%d)", 134262306a36Sopenharmony_ci UCAN_DRIVER_NAME, iface_desc->desc.bNumEndpoints); 134362306a36Sopenharmony_ci goto err_firmware_needs_update; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci /* check interface endpoints */ 134762306a36Sopenharmony_ci in_ep_addr = 0; 134862306a36Sopenharmony_ci out_ep_addr = 0; 134962306a36Sopenharmony_ci in_ep_size = 0; 135062306a36Sopenharmony_ci out_ep_size = 0; 135162306a36Sopenharmony_ci for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { 135262306a36Sopenharmony_ci ep = &iface_desc->endpoint[i].desc; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0) && 135562306a36Sopenharmony_ci ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 135662306a36Sopenharmony_ci USB_ENDPOINT_XFER_BULK)) { 135762306a36Sopenharmony_ci /* In Endpoint */ 135862306a36Sopenharmony_ci in_ep_addr = ep->bEndpointAddress; 135962306a36Sopenharmony_ci in_ep_addr &= USB_ENDPOINT_NUMBER_MASK; 136062306a36Sopenharmony_ci in_ep_size = le16_to_cpu(ep->wMaxPacketSize); 136162306a36Sopenharmony_ci } else if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 136262306a36Sopenharmony_ci 0) && 136362306a36Sopenharmony_ci ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 136462306a36Sopenharmony_ci USB_ENDPOINT_XFER_BULK)) { 136562306a36Sopenharmony_ci /* Out Endpoint */ 136662306a36Sopenharmony_ci out_ep_addr = ep->bEndpointAddress; 136762306a36Sopenharmony_ci out_ep_addr &= USB_ENDPOINT_NUMBER_MASK; 136862306a36Sopenharmony_ci out_ep_size = le16_to_cpu(ep->wMaxPacketSize); 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* check if interface is sane */ 137362306a36Sopenharmony_ci if (!in_ep_addr || !out_ep_addr) { 137462306a36Sopenharmony_ci dev_err(&udev->dev, "%s: invalid endpoint configuration\n", 137562306a36Sopenharmony_ci UCAN_DRIVER_NAME); 137662306a36Sopenharmony_ci goto err_firmware_needs_update; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci if (in_ep_size < sizeof(struct ucan_message_in)) { 137962306a36Sopenharmony_ci dev_err(&udev->dev, "%s: invalid in_ep MaxPacketSize\n", 138062306a36Sopenharmony_ci UCAN_DRIVER_NAME); 138162306a36Sopenharmony_ci goto err_firmware_needs_update; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci if (out_ep_size < sizeof(struct ucan_message_out)) { 138462306a36Sopenharmony_ci dev_err(&udev->dev, "%s: invalid out_ep MaxPacketSize\n", 138562306a36Sopenharmony_ci UCAN_DRIVER_NAME); 138662306a36Sopenharmony_ci goto err_firmware_needs_update; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci /* Stage 2 - Device Identification 139062306a36Sopenharmony_ci * ------------------------------- 139162306a36Sopenharmony_ci * 139262306a36Sopenharmony_ci * The device interface seems to be a ucan device. Do further 139362306a36Sopenharmony_ci * compatibility checks. On error probing is aborted, on 139462306a36Sopenharmony_ci * success this stage leaves the ctl_msg_buffer with the 139562306a36Sopenharmony_ci * reported contents of a GET_INFO command (supported 139662306a36Sopenharmony_ci * bittimings, tx_fifo depth). This information is used in 139762306a36Sopenharmony_ci * Stage 3 for the final driver initialisation. 139862306a36Sopenharmony_ci */ 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci /* Prepare Memory for control transfers */ 140162306a36Sopenharmony_ci ctl_msg_buffer = devm_kzalloc(&udev->dev, 140262306a36Sopenharmony_ci sizeof(union ucan_ctl_payload), 140362306a36Sopenharmony_ci GFP_KERNEL); 140462306a36Sopenharmony_ci if (!ctl_msg_buffer) { 140562306a36Sopenharmony_ci dev_err(&udev->dev, 140662306a36Sopenharmony_ci "%s: failed to allocate control pipe memory\n", 140762306a36Sopenharmony_ci UCAN_DRIVER_NAME); 140862306a36Sopenharmony_ci return -ENOMEM; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* get protocol version 141262306a36Sopenharmony_ci * 141362306a36Sopenharmony_ci * note: ucan_ctrl_command_* wrappers cannot be used yet 141462306a36Sopenharmony_ci * because `up` is initialised in Stage 3 141562306a36Sopenharmony_ci */ 141662306a36Sopenharmony_ci ret = usb_control_msg(udev, 141762306a36Sopenharmony_ci usb_rcvctrlpipe(udev, 0), 141862306a36Sopenharmony_ci UCAN_COMMAND_GET, 141962306a36Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | 142062306a36Sopenharmony_ci USB_RECIP_INTERFACE, 142162306a36Sopenharmony_ci UCAN_COMMAND_GET_PROTOCOL_VERSION, 142262306a36Sopenharmony_ci iface_desc->desc.bInterfaceNumber, 142362306a36Sopenharmony_ci ctl_msg_buffer, 142462306a36Sopenharmony_ci sizeof(union ucan_ctl_payload), 142562306a36Sopenharmony_ci UCAN_USB_CTL_PIPE_TIMEOUT); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci /* older firmware version do not support this command - those 142862306a36Sopenharmony_ci * are not supported by this drive 142962306a36Sopenharmony_ci */ 143062306a36Sopenharmony_ci if (ret != 4) { 143162306a36Sopenharmony_ci dev_err(&udev->dev, 143262306a36Sopenharmony_ci "%s: could not read protocol version, ret=%d\n", 143362306a36Sopenharmony_ci UCAN_DRIVER_NAME, ret); 143462306a36Sopenharmony_ci if (ret >= 0) 143562306a36Sopenharmony_ci ret = -EINVAL; 143662306a36Sopenharmony_ci goto err_firmware_needs_update; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci /* this driver currently supports protocol version 3 only */ 144062306a36Sopenharmony_ci protocol_version = 144162306a36Sopenharmony_ci le32_to_cpu(ctl_msg_buffer->cmd_get_protocol_version.version); 144262306a36Sopenharmony_ci if (protocol_version < UCAN_PROTOCOL_VERSION_MIN || 144362306a36Sopenharmony_ci protocol_version > UCAN_PROTOCOL_VERSION_MAX) { 144462306a36Sopenharmony_ci dev_err(&udev->dev, 144562306a36Sopenharmony_ci "%s: device protocol version %d is not supported\n", 144662306a36Sopenharmony_ci UCAN_DRIVER_NAME, protocol_version); 144762306a36Sopenharmony_ci goto err_firmware_needs_update; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci /* request the device information and store it in ctl_msg_buffer 145162306a36Sopenharmony_ci * 145262306a36Sopenharmony_ci * note: ucan_ctrl_command_* wrappers cannot be used yet 145362306a36Sopenharmony_ci * because `up` is initialised in Stage 3 145462306a36Sopenharmony_ci */ 145562306a36Sopenharmony_ci ret = usb_control_msg(udev, 145662306a36Sopenharmony_ci usb_rcvctrlpipe(udev, 0), 145762306a36Sopenharmony_ci UCAN_COMMAND_GET, 145862306a36Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | 145962306a36Sopenharmony_ci USB_RECIP_INTERFACE, 146062306a36Sopenharmony_ci UCAN_COMMAND_GET_INFO, 146162306a36Sopenharmony_ci iface_desc->desc.bInterfaceNumber, 146262306a36Sopenharmony_ci ctl_msg_buffer, 146362306a36Sopenharmony_ci sizeof(ctl_msg_buffer->cmd_get_device_info), 146462306a36Sopenharmony_ci UCAN_USB_CTL_PIPE_TIMEOUT); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci if (ret < 0) { 146762306a36Sopenharmony_ci dev_err(&udev->dev, "%s: failed to retrieve device info\n", 146862306a36Sopenharmony_ci UCAN_DRIVER_NAME); 146962306a36Sopenharmony_ci goto err_firmware_needs_update; 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci if (ret < sizeof(ctl_msg_buffer->cmd_get_device_info)) { 147262306a36Sopenharmony_ci dev_err(&udev->dev, "%s: device reported invalid device info\n", 147362306a36Sopenharmony_ci UCAN_DRIVER_NAME); 147462306a36Sopenharmony_ci goto err_firmware_needs_update; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci if (ctl_msg_buffer->cmd_get_device_info.tx_fifo == 0) { 147762306a36Sopenharmony_ci dev_err(&udev->dev, 147862306a36Sopenharmony_ci "%s: device reported invalid tx-fifo size\n", 147962306a36Sopenharmony_ci UCAN_DRIVER_NAME); 148062306a36Sopenharmony_ci goto err_firmware_needs_update; 148162306a36Sopenharmony_ci } 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci /* Stage 3 - Driver Initialisation 148462306a36Sopenharmony_ci * ------------------------------- 148562306a36Sopenharmony_ci * 148662306a36Sopenharmony_ci * Register device to Linux, prepare private structures and 148762306a36Sopenharmony_ci * reset the device. 148862306a36Sopenharmony_ci */ 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* allocate driver resources */ 149162306a36Sopenharmony_ci netdev = alloc_candev(sizeof(struct ucan_priv), 149262306a36Sopenharmony_ci ctl_msg_buffer->cmd_get_device_info.tx_fifo); 149362306a36Sopenharmony_ci if (!netdev) { 149462306a36Sopenharmony_ci dev_err(&udev->dev, 149562306a36Sopenharmony_ci "%s: cannot allocate candev\n", UCAN_DRIVER_NAME); 149662306a36Sopenharmony_ci return -ENOMEM; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci up = netdev_priv(netdev); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci /* initialize data */ 150262306a36Sopenharmony_ci up->udev = udev; 150362306a36Sopenharmony_ci up->netdev = netdev; 150462306a36Sopenharmony_ci up->intf_index = iface_desc->desc.bInterfaceNumber; 150562306a36Sopenharmony_ci up->in_ep_addr = in_ep_addr; 150662306a36Sopenharmony_ci up->out_ep_addr = out_ep_addr; 150762306a36Sopenharmony_ci up->in_ep_size = in_ep_size; 150862306a36Sopenharmony_ci up->ctl_msg_buffer = ctl_msg_buffer; 150962306a36Sopenharmony_ci up->context_array = NULL; 151062306a36Sopenharmony_ci up->available_tx_urbs = 0; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci up->can.state = CAN_STATE_STOPPED; 151362306a36Sopenharmony_ci up->can.bittiming_const = &up->device_info.bittiming_const; 151462306a36Sopenharmony_ci up->can.do_set_bittiming = ucan_set_bittiming; 151562306a36Sopenharmony_ci up->can.do_set_mode = &ucan_set_mode; 151662306a36Sopenharmony_ci spin_lock_init(&up->context_lock); 151762306a36Sopenharmony_ci spin_lock_init(&up->echo_skb_lock); 151862306a36Sopenharmony_ci netdev->netdev_ops = &ucan_netdev_ops; 151962306a36Sopenharmony_ci netdev->ethtool_ops = &ucan_ethtool_ops; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci usb_set_intfdata(intf, up); 152262306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &intf->dev); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* parse device information 152562306a36Sopenharmony_ci * the data retrieved in Stage 2 is still available in 152662306a36Sopenharmony_ci * up->ctl_msg_buffer 152762306a36Sopenharmony_ci */ 152862306a36Sopenharmony_ci ucan_parse_device_info(up, &ctl_msg_buffer->cmd_get_device_info); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci /* just print some device information - if available */ 153162306a36Sopenharmony_ci ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0, 153262306a36Sopenharmony_ci sizeof(union ucan_ctl_payload)); 153362306a36Sopenharmony_ci if (ret > 0) { 153462306a36Sopenharmony_ci /* copy string while ensuring zero termination */ 153562306a36Sopenharmony_ci strscpy(firmware_str, up->ctl_msg_buffer->raw, 153662306a36Sopenharmony_ci sizeof(union ucan_ctl_payload) + 1); 153762306a36Sopenharmony_ci } else { 153862306a36Sopenharmony_ci strcpy(firmware_str, "unknown"); 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci /* device is compatible, reset it */ 154262306a36Sopenharmony_ci ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0); 154362306a36Sopenharmony_ci if (ret < 0) 154462306a36Sopenharmony_ci goto err_free_candev; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci init_usb_anchor(&up->rx_urbs); 154762306a36Sopenharmony_ci init_usb_anchor(&up->tx_urbs); 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci up->can.state = CAN_STATE_STOPPED; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci /* register the device */ 155262306a36Sopenharmony_ci ret = register_candev(netdev); 155362306a36Sopenharmony_ci if (ret) 155462306a36Sopenharmony_ci goto err_free_candev; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci /* initialisation complete, log device info */ 155762306a36Sopenharmony_ci netdev_info(up->netdev, "registered device\n"); 155862306a36Sopenharmony_ci netdev_info(up->netdev, "firmware string: %s\n", firmware_str); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci /* success */ 156162306a36Sopenharmony_ci return 0; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_cierr_free_candev: 156462306a36Sopenharmony_ci free_candev(netdev); 156562306a36Sopenharmony_ci return ret; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_cierr_firmware_needs_update: 156862306a36Sopenharmony_ci dev_err(&udev->dev, 156962306a36Sopenharmony_ci "%s: probe failed; try to update the device firmware\n", 157062306a36Sopenharmony_ci UCAN_DRIVER_NAME); 157162306a36Sopenharmony_ci return -ENODEV; 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci/* disconnect the device */ 157562306a36Sopenharmony_cistatic void ucan_disconnect(struct usb_interface *intf) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci struct ucan_priv *up = usb_get_intfdata(intf); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci if (up) { 158262306a36Sopenharmony_ci unregister_candev(up->netdev); 158362306a36Sopenharmony_ci free_candev(up->netdev); 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic struct usb_device_id ucan_table[] = { 158862306a36Sopenharmony_ci /* Mule (soldered onto compute modules) */ 158962306a36Sopenharmony_ci {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425a, 0)}, 159062306a36Sopenharmony_ci /* Seal (standalone USB stick) */ 159162306a36Sopenharmony_ci {USB_DEVICE_INTERFACE_NUMBER(0x2294, 0x425b, 0)}, 159262306a36Sopenharmony_ci {} /* Terminating entry */ 159362306a36Sopenharmony_ci}; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ucan_table); 159662306a36Sopenharmony_ci/* driver callbacks */ 159762306a36Sopenharmony_cistatic struct usb_driver ucan_driver = { 159862306a36Sopenharmony_ci .name = UCAN_DRIVER_NAME, 159962306a36Sopenharmony_ci .probe = ucan_probe, 160062306a36Sopenharmony_ci .disconnect = ucan_disconnect, 160162306a36Sopenharmony_ci .id_table = ucan_table, 160262306a36Sopenharmony_ci}; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_cimodule_usb_driver(ucan_driver); 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 160762306a36Sopenharmony_ciMODULE_AUTHOR("Martin Elshuber <martin.elshuber@theobroma-systems.com>"); 160862306a36Sopenharmony_ciMODULE_AUTHOR("Jakob Unterwurzacher <jakob.unterwurzacher@theobroma-systems.com>"); 160962306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Theobroma Systems UCAN devices"); 1610