// SPDX-License-Identifier: GPL-2.0 /* Driver for Theobroma Systems UCAN devices, Protocol Version 3 * * Copyright (C) 2018 Theobroma Systems Design und Consulting GmbH * * * General Description: * * The USB Device uses three Endpoints: * * CONTROL Endpoint: Is used the setup the device (start, stop, * info, configure). * * IN Endpoint: The device sends CAN Frame Messages and Device * Information using the IN endpoint. * * OUT Endpoint: The driver sends configuration requests, and CAN * Frames on the out endpoint. * * Error Handling: * * If error reporting is turned on the device encodes error into CAN * error frames (see uapi/linux/can/error.h) and sends it using the * IN Endpoint. The driver updates statistics and forward it. */ #include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/error.h> #include <linux/ethtool.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/signal.h> #include <linux/skbuff.h> #include <linux/slab.h> #include <linux/usb.h> #define UCAN_DRIVER_NAME … #define UCAN_MAX_RX_URBS … /* the CAN controller needs a while to enable/disable the bus */ #define UCAN_USB_CTL_PIPE_TIMEOUT … /* this driver currently supports protocol version 3 only */ #define UCAN_PROTOCOL_VERSION_MIN … #define UCAN_PROTOCOL_VERSION_MAX … /* UCAN Message Definitions * ------------------------ * * ucan_message_out_t and ucan_message_in_t define the messages * transmitted on the OUT and IN endpoint. * * Multibyte fields are transmitted with little endianness * * INTR Endpoint: a single uint32_t storing the current space in the fifo * * OUT Endpoint: single message of type ucan_message_out_t is * transmitted on the out endpoint * * IN Endpoint: multiple messages ucan_message_in_t concateted in * the following way: * * m[n].len <=> the length if message n(including the header in bytes) * m[n] is is aligned to a 4 byte boundary, hence * offset(m[0]) := 0; * offset(m[n+1]) := offset(m[n]) + (m[n].len + 3) & 3 * * this implies that * offset(m[n]) % 4 <=> 0 */ /* Device Global Commands */ enum { … }; /* UCAN Commands */ enum { … }; /* UCAN_COMMAND_START and UCAN_COMMAND_GET_INFO operation modes (bitmap). * Undefined bits must be set to 0. */ enum { … }; /* UCAN_COMMAND_GET subcommands */ enum { … }; /* UCAN_COMMAND_FILTER subcommands */ enum { … }; /* OUT endpoint message types */ enum { … }; /* IN endpoint message types */ enum { … }; struct ucan_ctl_cmd_start { … } __packed; struct ucan_ctl_cmd_set_bittiming { … } __packed; struct ucan_ctl_cmd_device_info { … } __packed; struct ucan_ctl_cmd_get_protocol_version { … } __packed; ucan_ctl_payload __packed; enum { … }; /* Transmission Complete within ucan_message_in */ struct ucan_tx_complete_entry_t { … } __packed __aligned(…); /* CAN Data message format within ucan_message_in/out */ struct ucan_can_msg { … } __packed; /* OUT Endpoint, outbound messages */ struct ucan_message_out { … } __packed __aligned(…); /* IN Endpoint, inbound messages */ struct ucan_message_in { … } __packed __aligned(…); /* Macros to calculate message lengths */ #define UCAN_OUT_HDR_SIZE … #define UCAN_IN_HDR_SIZE … #define UCAN_IN_LEN(member) … struct ucan_priv; /* Context Information for transmission URBs */ struct ucan_urb_context { … }; /* Information reported by the USB device */ struct ucan_device_info { … }; /* Driver private data */ struct ucan_priv { … }; static u8 ucan_can_cc_dlc2len(struct ucan_can_msg *msg, u16 len) { … } static void ucan_release_context_array(struct ucan_priv *up) { … } static int ucan_alloc_context_array(struct ucan_priv *up) { … } static struct ucan_urb_context *ucan_alloc_context(struct ucan_priv *up) { … } static bool ucan_release_context(struct ucan_priv *up, struct ucan_urb_context *ctx) { … } static int ucan_ctrl_command_out(struct ucan_priv *up, u8 cmd, u16 subcmd, u16 datalen) { … } static int ucan_device_request_in(struct ucan_priv *up, u8 cmd, u16 subcmd, u16 datalen) { … } /* Parse the device information structure reported by the device and * setup private variables accordingly */ static void ucan_parse_device_info(struct ucan_priv *up, struct ucan_ctl_cmd_device_info *device_info) { … } /* Handle a CAN error frame that we have received from the device. * Returns true if the can state has changed. */ static bool ucan_handle_error_frame(struct ucan_priv *up, struct ucan_message_in *m, canid_t canid) { … } /* Callback on reception of a can frame via the IN endpoint * * This function allocates an skb and transferres it to the Linux * network stack */ static void ucan_rx_can_msg(struct ucan_priv *up, struct ucan_message_in *m) { … } /* callback indicating completed transmission */ static void ucan_tx_complete_msg(struct ucan_priv *up, struct ucan_message_in *m) { … } /* callback on reception of a USB message */ static void ucan_read_bulk_callback(struct urb *urb) { … } /* callback after transmission of a USB message */ static void ucan_write_bulk_callback(struct urb *urb) { … } static void ucan_cleanup_rx_urbs(struct ucan_priv *up, struct urb **urbs) { … } static int ucan_prepare_and_anchor_rx_urbs(struct ucan_priv *up, struct urb **urbs) { … } /* Submits rx urbs with the semantic: Either submit all, or cleanup * everything. I case of errors submitted urbs are killed and all urbs in * the array are freed. I case of no errors every entry in the urb * array is set to NULL. */ static int ucan_submit_rx_urbs(struct ucan_priv *up, struct urb **urbs) { … } /* Open the network device */ static int ucan_open(struct net_device *netdev) { … } static struct urb *ucan_prepare_tx_urb(struct ucan_priv *up, struct ucan_urb_context *context, struct can_frame *cf, u8 echo_index) { … } static void ucan_clean_up_tx_urb(struct ucan_priv *up, struct urb *urb) { … } /* callback when Linux needs to send a can frame */ static netdev_tx_t ucan_start_xmit(struct sk_buff *skb, struct net_device *netdev) { … } /* Device goes down * * Clean up used resources */ static int ucan_close(struct net_device *netdev) { … } /* CAN driver callbacks */ static const struct net_device_ops ucan_netdev_ops = …; static const struct ethtool_ops ucan_ethtool_ops = …; /* Request to set bittiming * * This function generates an USB set bittiming message and transmits * it to the device */ static int ucan_set_bittiming(struct net_device *netdev) { … } /* Restart the device to get it out of BUS-OFF state. * Called when the user runs "ip link set can1 type can restart". */ static int ucan_set_mode(struct net_device *netdev, enum can_mode mode) { … } /* Probe the device, reset it and gather general device information */ static int ucan_probe(struct usb_interface *intf, const struct usb_device_id *id) { … } /* disconnect the device */ static void ucan_disconnect(struct usb_interface *intf) { … } static struct usb_device_id ucan_table[] = …; MODULE_DEVICE_TABLE(usb, ucan_table); /* driver callbacks */ static struct usb_driver ucan_driver = …; module_usb_driver(…) …; MODULE_LICENSE(…) …; MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …;