summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Elshuber <martin.elshuber@theobroma-systems.com>2015-10-16 11:57:05 +0200
committerKlaus Goger <klaus.goger@theobroma-systems.com>2016-08-17 14:54:38 +0200
commit211ff469dfaf7ae626200b9d524ec80afb5f0e75 (patch)
tree19b5b24598d81b768986fe7f5cf3b8461c564876
parent6c1da56ab65cc3d4851be73d577ec7feafad160a (diff)
can: ucan: added support for UCAN devices
-rw-r--r--drivers/net/can/Kconfig9
-rw-r--r--drivers/net/can/dev.c32
-rw-r--r--drivers/net/can/usb/Kconfig7
-rw-r--r--drivers/net/can/usb/Makefile1
-rw-r--r--drivers/net/can/usb/ucan.c1062
-rw-r--r--drivers/net/can/usb/ucan.h216
-rw-r--r--include/linux/can/dev.h9
-rw-r--r--include/uapi/linux/can/netlink.h22
8 files changed, 1356 insertions, 2 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 0d40aef928e2..5b0dc4195502 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -52,6 +52,15 @@ config CAN_CALC_BITTIMING
arguments "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw".
If unsure, say Y.
+config CAN_HWFILTER
+ bool "Enable CAN hardware filtering support"
+ default n
+ ---help---
+ This enables, netlink options to configure CAN hardware
+ filtering. This feature is supported by the uCAN
+ driver. If enabled hardware filters can be set with the a
+ patched version of ip(8).
+
config CAN_LEDS
bool "Enable LED triggers for Netlink based drivers"
depends on LEDS_CLASS
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 141c2a42d7ed..d091be10ed72 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -871,6 +871,30 @@ static int can_changelink(struct net_device *dev,
}
}
+#ifdef CONFIG_CAN_HWFILTER
+ if (data[IFLA_CAN_HARDWARE_FILTER]) {
+ struct can_hwfilter cf;
+
+ /* Do not allow changing hardware filter while running */
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ memcpy(&cf, nla_data(data[IFLA_CAN_HARDWARE_FILTER]), sizeof(cf));
+
+ if (cf.count > priv->hwfilterbanks)
+ return -EINVAL;
+
+ memcpy(&priv->filter, &cf, sizeof(cf));
+
+ if (priv->do_set_hwfilter) {
+ /* Finally, set the bit-timing registers */
+ err = priv->do_set_hwfilter(dev);
+ if (err)
+ return err;
+ }
+ }
+#endif
+
return 0;
}
@@ -931,7 +955,13 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
(priv->data_bittiming_const &&
nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST,
sizeof(*priv->data_bittiming_const),
- priv->data_bittiming_const)))
+ priv->data_bittiming_const))
+#ifdef CONFIG_CAN_HWFILTER
+ || nla_put(skb, IFLA_CAN_HARDWARE_FILTER,
+ sizeof(priv->filter),
+ &priv->filter)
+#endif
+ )
return -EMSGSIZE;
return 0;
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index bcb272f6c68a..2334170d6686 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -78,4 +78,11 @@ config CAN_8DEV_USB
This driver supports the USB2CAN interface
from 8 devices (http://www.8devices.com).
+config CAN_UCAN
+ tristate "uCAN Device Driver"
+ ---help---
+ This driver supports UCAN based USB CAN devices
+
+ * STM32 based CAN controller on A31 uQ7 (Pangolin)
+
endmenu
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index a64cf983fb87..3afde989139b 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
+obj-$(CONFIG_CAN_UCAN) += ucan.o
diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c
new file mode 100644
index 000000000000..e1fd896a29e9
--- /dev/null
+++ b/drivers/net/can/usb/ucan.c
@@ -0,0 +1,1062 @@
+/*
+ * CAN driver for uCAN
+ *
+ * Copyright (C) 2015 Theobroma Systems Design und Consulting GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * This driver is inspired by the 4.0.0 version of drivers/net/can/usb/ems_usb.c
+ *
+ */
+
+/*****************************************************
+ * General Description:
+ *****************************************************
+ *
+ * The USB Device uses three Endpoints:
+ *
+ * Interrupt Endpoint: Once the device is started the device sends
+ * its TX FIFO status (space left) on this endpoint. The driver uses
+ * this information for flow control.
+ *
+ * IN Enpoint: 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/signal.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include "ucan.h"
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Martin Elshuber, Theobroma Systems Design und Consulting GmbH"
+ "<martin.elshuber@theobroma-systems.com>");
+MODULE_DESCRIPTION("CAN driver for uCAN devices");
+
+#define USB_CPCUSB_VENDOR_ID 0x0483
+#define USB_CPCUSB_UCAN_PRODUCT_ID 0x5720
+
+#define MAX_TX_URBS 8
+#define MAX_RX_URBS 8
+#define TX_QUEUE_STOP_TRESHOLD 1
+
+static struct usb_device_id uCAN_table[] = {
+ {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_UCAN_PRODUCT_ID)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, uCAN_table);
+
+struct uCAN;
+
+/* Context Information for transmission URBs */
+struct uCAN_urb_context {
+ struct uCAN *up;
+ u32 echo_index;
+ int size;
+ u8 dlc;
+};
+
+/* Information reported by the USB device */
+struct uCAN_device_info {
+ struct can_bittiming_const bittiming_const;
+ int tx_fifo;
+};
+
+/* Driver private data */
+struct uCAN {
+ struct can_priv can; /* must be the first member */
+
+ struct usb_device *udev;
+ struct net_device *netdev;
+
+ struct usb_endpoint_descriptor *out_ep;
+ struct usb_endpoint_descriptor *in_ep;
+ struct usb_endpoint_descriptor *irq_ep;
+
+ struct usb_anchor rx_urbs;
+ struct usb_anchor tx_urbs;
+ struct urb *irq_urb;
+ u32 *irq_data;
+
+ u8 *tx_msg_buffer;
+ u8 *rx_msg_buffer;
+
+ struct uCAN_device_info device_info;
+
+ atomic_t active_tx_urbs;
+ struct uCAN_urb_context tx_urb_contexts[MAX_TX_URBS];
+ int free_slots;
+};
+
+/* Sends a command to the device */
+static int uCAN_command(struct uCAN *up, u8 cmd, u8 subcmd, u16 value)
+{
+ int len;
+ struct uCAN_message_out *m = (struct uCAN_message_out *)up->tx_msg_buffer;
+ dev_dbg(&up->udev->dev, "%s\n", __func__);
+
+ m->type = __cpu_to_le16(UCAN_OUT_COMMAND);
+ m->len = __cpu_to_le16(UCAN_OUT_LEN(m->msg.command));
+ m->msg.command.cmd = cmd;
+ m->msg.command.subcmd = subcmd;
+ m->msg.command.val = __cpu_to_le16(value);
+
+ return usb_bulk_msg(up->udev,
+ usb_sndbulkpipe(up->udev, up->out_ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK),
+ m,
+ __le16_to_cpu(m->len),
+ &len, 1000);
+}
+
+/* Request the device Information */
+static int uCAN_get_device_info(struct uCAN *up)
+{
+ int ret;
+ int len;
+ struct uCAN_message_in *m = (struct uCAN_message_in *)up->rx_msg_buffer;
+ dev_dbg(&up->udev->dev, "%s\n", __func__);
+
+ // send the request command
+ ret = uCAN_command(up, UCAN_COMMAND_GET,
+ UCAN_COMMAND_GET_INFO, 0);
+ if (ret)
+ return ret;
+
+ // retrieve the information
+ ret = usb_bulk_msg(up->udev,
+ usb_rcvbulkpipe(up->udev,
+ up->in_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ m,
+ up->in_ep->wMaxPacketSize,
+ &len, 1000);
+ if (ret)
+ return ret;
+
+ // check sanity
+ if (m->type!=__cpu_to_le16(UCAN_IN_DEVICE_INFO))
+ return -EINVAL;
+
+ if (__le16_to_cpu(m->len) != UCAN_IN_LEN(m->msg.device_info) )
+ return -EINVAL;
+
+ // store the data
+ strcpy(up->device_info.bittiming_const.name, "uCAN");
+ up->can.clock.freq = __le32_to_cpu(m->msg.device_info.freq);
+ up->device_info.tx_fifo = m->msg.device_info.tx_fifo;
+ up->device_info.bittiming_const.tseg1_min = m->msg.device_info.tseg1_min;
+ up->device_info.bittiming_const.tseg1_max = m->msg.device_info.tseg1_max;
+ up->device_info.bittiming_const.tseg2_min = m->msg.device_info.tseg2_min;
+ up->device_info.bittiming_const.tseg2_max = m->msg.device_info.tseg2_max;
+ up->device_info.bittiming_const.sjw_max = m->msg.device_info.sjw_max;
+ up->device_info.bittiming_const.brp_min = __le32_to_cpu(m->msg.device_info.brp_min);
+ up->device_info.bittiming_const.brp_max = __le32_to_cpu(m->msg.device_info.brp_max);
+ up->device_info.bittiming_const.brp_inc = __le16_to_cpu(m->msg.device_info.brp_inc);
+#ifdef CONFIG_CAN_HWFILTER
+ up->can.hwfilterbanks = __le16_to_cpu(m->msg.device_info.hwfilter);
+ up->can.rxmailboxes = __le16_to_cpu(m->msg.device_info.rxmboxes);
+#endif
+
+ up->can.ctrlmode_supported = 0;
+
+ if (__le16_to_cpu(m->msg.device_info.ctrlmodes) & UCAN_MODE_LOOPBACK )
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK;
+ if (__le16_to_cpu(m->msg.device_info.ctrlmodes) & UCAN_MODE_SILENT )
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+ if (__le16_to_cpu(m->msg.device_info.ctrlmodes) & UCAN_MODE_3_SAMPLES )
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
+ if (__le16_to_cpu(m->msg.device_info.ctrlmodes) & UCAN_MODE_ONE_SHOT )
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT;
+ if (__le16_to_cpu(m->msg.device_info.ctrlmodes) & UCAN_MODE_BERR_REPORT )
+ up->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING;
+
+ return 0;
+}
+
+/* Callback when the device sends the IRQ sate *
+ *
+ * This function simply stores the current TX fifo state for flow
+ * control */
+static void uCAN_read_irq_callback(struct urb *urb)
+{
+ int ret;
+ struct uCAN *up = urb->context;
+ struct net_device *netdev = up->netdev;
+
+ dev_dbg(&up->udev->dev, "%s %d\n", __func__,urb->status );
+
+ switch (urb->status) {
+ case 0:
+ ACCESS_ONCE(up->free_slots) = __le32_to_cpu(*up->irq_data);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ETIME:
+ dev_dbg(&up->udev->dev, "%s ENOENT|ESHUTDOWN|ETIME\n", __func__);
+ return;
+ default:
+ dev_warn(&up->udev->dev, "%s error (%d)\n", __func__, urb->status);
+ break;
+ }
+
+ usb_fill_int_urb(urb,
+ up->udev,
+ usb_rcvintpipe(up->udev,
+ up->irq_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ urb->transfer_buffer,
+ up->irq_ep->wMaxPacketSize,
+ uCAN_read_irq_callback,
+ up,
+ up->irq_ep->bInterval);
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+
+ if (ret == -ENODEV)
+ netif_device_detach(netdev);
+ else if (ret)
+ dev_err(&up->udev->dev,
+ "failed resubmitting read bulk urb: %d\n", ret);
+}
+
+/* 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 *up, struct uCAN_message_in *m)
+{
+ int len;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats = &up->netdev->stats;
+
+ dev_dbg(&up->udev->dev, "%s\n", __func__);
+
+ // get the contents of the length field
+ len = __le16_to_cpu(m->len);
+
+ // check sanity
+ if (len<UCAN_IN_HDR_SIZE+sizeof(m->msg.can_msg.id)) {
+ dev_warn(&up->udev->dev, "invalid input message len\n");
+ return;
+ }
+
+ // allocate skb
+ skb = alloc_can_skb(up->netdev, &cf);
+ if (skb == NULL)
+ return;
+
+ // fill the can frame
+ cf->can_id = __le32_to_cpu(m->msg.can_msg.id);
+ cf->can_dlc = len - (UCAN_IN_HDR_SIZE+sizeof(m->msg.can_msg.id));
+
+ if (cf->can_dlc > sizeof(m->msg.can_msg.data))
+ goto err_freeskb;
+
+ if (cf->can_dlc < 0)
+ goto err_freeskb;
+
+ if (cf->can_id & CAN_EFF_FLAG )
+ cf->can_id &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_ERR_FLAG);
+ else
+ cf->can_id &= (CAN_SFF_MASK | CAN_RTR_FLAG | CAN_ERR_FLAG);
+
+
+ if (cf->can_id & CAN_RTR_FLAG ) {
+ cf->can_id |= CAN_RTR_FLAG;
+ cf->can_dlc = m->msg.can_rtr_msg.dlc;
+ } else {
+ memcpy(cf->data, m->msg.can_msg.data, cf->can_dlc);
+ }
+
+ // handle can error frames
+ if (cf->can_id & CAN_ERR_FLAG ) {
+ if ( cf->can_id & CAN_ERR_BUSOFF ) {
+ up->can.can_stats.bus_off++;
+ can_bus_off(up->netdev);
+ }
+
+ if ( cf->data[1] & ( CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING ) ) {
+ up->can.can_stats.error_warning++;
+ }
+
+ if ( cf->data[1] & ( CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING ) ) {
+ up->can.can_stats.error_passive++;
+ }
+
+ if ( cf->data[1] & CAN_ERR_CRTL_RX_OVERFLOW )
+ stats->rx_over_errors++;
+
+ if ( cf->can_id & CAN_ERR_LOSTARB )
+ up->can.can_stats.arbitration_lost++;
+
+ if ( cf->can_id & CAN_ERR_BUSERROR )
+ up->can.can_stats.bus_error++;
+
+ if ( cf->data[2] & CAN_ERR_PROT_TX ) {
+ stats->tx_errors++;
+ }
+ else {
+ stats->rx_errors++;
+ }
+ cf->can_id |= CAN_ERR_FLAG;
+ }
+
+ // pass it to Linux
+ netif_receive_skb(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return;
+err_freeskb:
+ kfree_skb(skb);
+}
+
+/* callback on reception of a USB message */
+static void uCAN_read_bulk_callback(struct urb *urb)
+{
+ int ret;
+ int len;
+ int pos;
+ struct uCAN *up = urb->context;
+ struct net_device *netdev = up->netdev;
+ struct uCAN_message_in *m;
+
+ dev_dbg(&up->udev->dev, "%s %p\n", __func__, up);
+
+ // check URB status
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ETIME:
+ dev_dbg(&up->udev->dev, "%s ENOENT|ESHUTDOWN|ETIME\n", __func__);
+ return;
+ default:
+ goto resubmit;
+ }
+
+ // iterate ove input
+ pos = 0;
+ while (pos < urb->actual_length) {
+
+ // check sanity
+ if ( ( urb->actual_length - pos ) < UCAN_IN_HDR_SIZE) {
+ dev_warn(&up->udev->dev, "invalid input message 1 %d\n", urb->actual_length);
+ goto resubmit;
+ }
+
+ // get the pointer to the message
+ m = (struct uCAN_message_in *)( ( (u8*)urb->transfer_buffer ) + pos );
+ len = __le16_to_cpu(m->len);
+
+ // check sanity
+ if (len > (urb->actual_length - pos) ) {
+ dev_warn(&up->udev->dev, "invalid input message 2 %d\n", len);
+ goto resubmit;
+ }
+
+ switch (__le16_to_cpu(m->type)) {
+ case UCAN_IN_RX:
+ uCAN_rx_can_msg(up, m);
+ break;
+ default:
+ dev_warn(&up->udev->dev, "invalid input message type\n");
+ break;
+ }
+
+ // proceed to next message
+ pos += len;
+ // allign to 4 byte boundary
+ pos = ( pos + 3 ) & ~3;
+ }
+
+resubmit:
+ // resubmit urb when done
+ usb_fill_bulk_urb(urb,
+ up->udev,
+ usb_rcvbulkpipe(up->udev,
+ up->in_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ urb->transfer_buffer,
+ up->in_ep->wMaxPacketSize,
+ uCAN_read_bulk_callback,
+ up);
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+
+ if (ret == -ENODEV)
+ netif_device_detach(netdev);
+ else if (ret)
+ dev_err(&up->udev->dev,
+ "failed resubmitting read bulk urb: %d\n", ret);
+
+}
+
+/* callback after transmission of a USB message */
+static void uCAN_write_bulk_callback(struct urb *urb)
+{
+ struct uCAN_urb_context *context = urb->context;
+ struct uCAN *up;
+
+ // get the urb context
+ BUG_ON(!context);
+ up = context->up;
+
+ // free up our allocated buffer
+ usb_free_coherent(urb->dev, context->size,
+ urb->transfer_buffer, urb->transfer_dma);
+
+ atomic_dec(&up->active_tx_urbs);
+
+ // sanity check
+ if (!netif_device_present(up->netdev))
+ return;
+
+ // urb state check
+ if (urb->status)
+ netdev_info(up->netdev, "Tx URB aborted (%d)\n", urb->status);
+
+ up->netdev->trans_start = jiffies;
+
+ // update statistics
+ up->netdev->stats.tx_packets++;
+ up->netdev->stats.tx_bytes += context->dlc;
+
+ // echo can frame
+ can_get_echo_skb(up->netdev, context->echo_index);
+
+ // Release context
+ context->echo_index = -1;
+
+ // restart the queue if necessary
+ if (netif_queue_stopped(up->netdev))
+ netif_wake_queue(up->netdev);
+}
+
+/* Open the network device */
+static int uCAN_open(struct net_device *netdev)
+{
+ int i;
+ int ret;
+ void *buf;
+ u16 ctrlmode;
+ struct urb *urb;
+ struct uCAN *up = netdev_priv(netdev);
+ dev_dbg(&up->udev->dev, "%s\n", __func__);
+
+ // call CAN layer open
+ ret = open_candev(netdev);
+ if (ret)
+ goto err;
+
+ ret = -ENOMEM;
+
+ // set the queue state as empty
+ up->free_slots = up->device_info.tx_fifo;
+
+ // initiallze IRQ endpoint
+ up->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!up->irq_urb) {
+ goto err;
+ }
+
+ up->irq_data = kzalloc(up->irq_ep->wMaxPacketSize, GFP_KERNEL);
+ if (!up->irq_data) {
+ usb_free_urb(up->irq_urb);
+ goto err;
+ }
+
+ usb_fill_int_urb(up->irq_urb,
+ up->udev,
+ usb_rcvintpipe(up->udev, up->irq_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ up->irq_data,
+ up->irq_ep->wMaxPacketSize,
+ uCAN_read_irq_callback,
+ up,
+ up->irq_ep->bInterval);
+
+ ret = usb_submit_urb(up->irq_urb, GFP_KERNEL);
+ if (ret) {
+ kfree(up->irq_data);
+ usb_free_urb(up->irq_urb);
+ goto err;
+ }
+
+ // intialize IN enpoint
+ for (i=0; i<MAX_RX_URBS; i++) {
+ ret = -ENOMEM;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ goto err;
+ }
+
+ buf = usb_alloc_coherent(up->udev,
+ up->in_ep->wMaxPacketSize,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ usb_free_urb(urb);
+ goto err;
+ }
+
+ usb_fill_bulk_urb(urb,
+ up->udev,
+ usb_rcvbulkpipe(up->udev,
+ up->in_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ buf,
+ up->in_ep->wMaxPacketSize,
+ uCAN_read_bulk_callback,
+ up);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &up->rx_urbs);
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+
+ if (ret) {
+ usb_unanchor_urb(urb);
+ usb_free_coherent(up->udev,
+ up->in_ep->wMaxPacketSize,
+ buf,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ goto err;
+ }
+
+ /* Drop reference, USB core will take care of freeing it */
+ usb_free_urb(urb);
+ }
+
+ // check the control mode
+ ctrlmode = 0;
+ if (up->can.ctrlmode & CAN_CTRLMODE_LOOPBACK )
+ ctrlmode |= UCAN_MODE_LOOPBACK;
+ if (up->can.ctrlmode & CAN_CTRLMODE_LISTENONLY )
+ ctrlmode |= UCAN_MODE_SILENT;
+ if (up->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES )
+ ctrlmode |= UCAN_MODE_3_SAMPLES;
+ if (up->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT )
+ ctrlmode |= UCAN_MODE_ONE_SHOT;
+ if (up->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING )
+ ctrlmode |= UCAN_MODE_BERR_REPORT;
+
+ // start the USB device
+ ret = uCAN_command(up, UCAN_COMMAND_START,0,__cpu_to_le16(ctrlmode));
+ if (ret)
+ goto err;
+
+ // start the network queue
+ netif_start_queue(netdev);
+
+ return 0;
+
+err:
+ usb_kill_anchored_urbs(&up->rx_urbs);
+ return ret;
+}
+
+/* callback when Linux needs to send a can frame */
+static netdev_tx_t uCAN_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ int i,ret;
+ struct urb *urb;
+ struct uCAN_urb_context *context;
+ struct uCAN *up = netdev_priv(netdev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct uCAN_message_out *m = (struct uCAN_message_out *)up->tx_msg_buffer;
+ size_t size = UCAN_OUT_HDR_SIZE+cf->can_dlc;
+
+ dev_dbg(&up->udev->dev, "%s\n", __func__);
+
+ // check skb
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ /* create a URB, and a buffer for it, and copy the data to the URB */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ goto drop;
+ }
+
+ m = usb_alloc_coherent(up->udev, size, GFP_ATOMIC, &urb->transfer_dma);
+ if (!m) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ goto drop;
+ }
+
+ // build the USB message
+ if (cf->can_dlc>sizeof(m->msg.can_msg.data))
+ cf->can_dlc = sizeof(m->msg.can_msg.data);
+
+ m->type = __cpu_to_le16(UCAN_OUT_TX);
+ m->msg.can_msg.id = __cpu_to_le32(cf->can_id);
+
+ if (cf->can_id & CAN_RTR_FLAG) {
+ m->len = __cpu_to_le16(UCAN_OUT_LEN(m->msg.can_rtr_msg));
+ m->msg.can_rtr_msg.dlc = cf->can_dlc;
+ }
+ else {
+ m->len = __cpu_to_le16(UCAN_OUT_HDR_SIZE+
+ sizeof(m->msg.can_msg.id)+cf->can_dlc);
+ memcpy(m->msg.can_msg.data, cf->data, cf->can_dlc);
+ }
+
+ // allocate a context
+ context = NULL;
+ for (i = 0; i < MAX_TX_URBS; i++) {
+ if (up->tx_urb_contexts[i].echo_index == -1) {
+ context = &up->tx_urb_contexts[i];
+ context->up = up;
+ context->echo_index=i;
+ context->size = size;
+ context->dlc = cf->can_dlc;
+ atomic_inc(&up->active_tx_urbs);
+ break;
+ }
+ }
+
+ WARN_ON_ONCE(!context);
+ if (!context) {
+ usb_free_coherent(up->udev, size, m, urb->transfer_dma);
+ usb_free_urb(urb);
+ goto drop;
+ }
+
+ // build the urb
+ usb_fill_bulk_urb(urb,
+ up->udev,
+ usb_sndbulkpipe(up->udev,
+ up->out_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ m,
+ __le16_to_cpu(m->len),
+ uCAN_write_bulk_callback,
+ context);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &up->tx_urbs);
+ can_put_echo_skb(skb, up->netdev, context->echo_index);
+
+ // transmit it
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret) {
+ // on error, clean up
+ can_free_echo_skb(up->netdev, context->echo_index);
+
+ usb_unanchor_urb(urb);
+ usb_free_coherent(up->udev, size, m, urb->transfer_dma);
+ dev_kfree_skb(skb);
+
+ context->echo_index = -1;
+ atomic_dec(&up->active_tx_urbs);
+
+ if (ret == -ENODEV) {
+ netif_device_detach(up->netdev);
+ } else {
+ netdev_warn(up->netdev, "failed tx_urb %d\n", ret);
+ up->netdev->stats.tx_dropped++;
+ }
+ } else {
+ netdev->trans_start = jiffies;
+
+ /* Slow down tx path, if fifo state is low */
+ if ( (atomic_read(&up->active_tx_urbs) >= MAX_TX_URBS)
+ || (ACCESS_ONCE(up->free_slots) < TX_QUEUE_STOP_TRESHOLD) )
+ {
+ netif_stop_queue(netdev);
+ }
+ }
+
+ // release ref, as we do not need the urb anymore
+ usb_free_urb(urb);
+
+ return 0;
+drop:
+ dev_kfree_skb(skb);
+ up->netdev->stats.tx_dropped++;
+
+ return NETDEV_TX_OK;
+}
+
+/* Device goes down
+ *
+ * Cleanup used resources */
+static int uCAN_close(struct net_device *netdev)
+{
+ int ret;
+ struct uCAN *up = netdev_priv(netdev);
+ dev_dbg(&up->udev->dev, "%s\n", __func__);
+
+ netif_stop_queue(netdev);
+
+ if ( (ret = uCAN_command(up, UCAN_COMMAND_STOP,0,0)) ) {
+ dev_warn(&up->udev->dev, "Could not stop USB CAN device code: %d\n", ret);
+ }
+
+ usb_kill_urb(up->irq_urb);
+ usb_kill_anchored_urbs(&up->rx_urbs);
+ usb_kill_anchored_urbs(&up->tx_urbs);
+ atomic_set(&up->active_tx_urbs, 0);
+
+ kfree(up->irq_data);
+
+ close_candev(up->netdev);
+ return 0;
+}
+
+/* CAN driver callbacks */
+static const struct net_device_ops uCAN_netdev_ops = {
+ .ndo_open = uCAN_open,
+ .ndo_stop = uCAN_close,
+ .ndo_start_xmit = uCAN_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+/* 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)
+{
+ int len;
+ struct uCAN *up = netdev_priv(netdev);
+ struct uCAN_message_out *m = (struct uCAN_message_out *)up->tx_msg_buffer;
+ dev_dbg(&up->udev->dev, "%s\n", __func__);
+
+ m->type = __cpu_to_le16(UCAN_OUT_SET_BITTIMING);
+ m->len = __cpu_to_le16(UCAN_OUT_LEN(m->msg.bittiming));
+ m->msg.bittiming.tq = __cpu_to_le32(up->can.bittiming.tq);
+ m->msg.bittiming.brp = __cpu_to_le16(up->can.bittiming.brp);
+ m->msg.bittiming.sample_point = __cpu_to_le32(up->can.bittiming.sample_point);
+ m->msg.bittiming.prop_seg = up->can.bittiming.prop_seg;
+ m->msg.bittiming.phase_seg1 = up->can.bittiming.phase_seg1;
+ m->msg.bittiming.phase_seg2 = up->can.bittiming.phase_seg2;
+ m->msg.bittiming.sjw = up->can.bittiming.sjw;
+
+ dev_dbg(&up->udev->dev,
+ "Setup bittiming\n"
+ " bitrate: %d\n"
+ " sample-point: %d\n"
+ " tq: %d\n"
+ " prop_seg: %d\n"
+ " phase_seg1 %d\n"
+ " phase_seg2 %d\n"
+ " sjw %d\n"
+ " brp %d\n",
+ up->can.bittiming.bitrate,
+ up->can.bittiming.sample_point,
+ up->can.bittiming.tq,
+ up->can.bittiming.prop_seg,
+ up->can.bittiming.phase_seg1,
+ up->can.bittiming.phase_seg2,
+ up->can.bittiming.sjw,
+ up->can.bittiming.brp);
+
+ return usb_bulk_msg(up->udev,
+ usb_sndbulkpipe(up->udev, up->out_ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK),
+ m,
+ __le16_to_cpu(m->len),
+ &len, 1000);
+}
+
+#ifdef CONFIG_CAN_HWFILTER
+/* Request to setup hardware filtering
+ *
+ * First al current harware filters are cleard.
+ * Second e seires of messages are sent to enable specific filters
+ */
+static int uCAN_set_hwfilter(struct net_device *netdev)
+{
+ int len;
+ int ret, i;
+ struct uCAN *up = netdev_priv(netdev);
+ struct uCAN_message_out *m = (struct uCAN_message_out *)up->tx_msg_buffer;
+
+ // clear filters
+ ret = uCAN_command(up, UCAN_COMMAND_FILTER, UCAN_FILTER_CLEAR, 0);
+ if (ret)
+ return ret;
+
+ // setup filters
+ if (up->can.filter.count) {
+ for (i = 0; i < up->can.filter.count; i++) {
+ m->type = __cpu_to_le16(UCAN_OUT_ENABLE_FILTER);
+ m->len = __cpu_to_le16(UCAN_OUT_LEN(m->msg.enable_filter));
+ m->msg.enable_filter.id = __cpu_to_le32(up->can.filter.filter[i].id);
+ m->msg.enable_filter.mask = __cpu_to_le32(up->can.filter.filter[i].mask);
+ m->msg.enable_filter.mbox = __cpu_to_le16(up->can.filter.filter[i].mbox);
+ dev_dbg(&up->udev->dev,
+ "Setup hwfilter [ (ID & %x) = ( %x & %x ) ] => #%d\n",
+ m->msg.enable_filter.mask,
+ m->msg.enable_filter.id,
+ m->msg.enable_filter.mask,
+ m->msg.enable_filter.mbox);
+ ret = usb_bulk_msg(up->udev,
+ usb_sndbulkpipe(up->udev,
+ up->out_ep->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK),
+ m,
+ __le16_to_cpu(m->len),
+ &len, 1000);
+ if (ret)
+ return ret;
+ }
+ }
+ else {
+ // disable flters if none are set
+ ret = uCAN_command(up, UCAN_COMMAND_FILTER, UCAN_FILTER_DISABLE, 0);
+ }
+ return 0;
+}
+#endif
+
+/* Probe the device, reset it and gather general device informations */
+static int uCAN_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret;
+ int i;
+ struct usb_device *udev;
+ struct net_device *netdev;
+ struct usb_host_interface *iface_desc;
+ struct uCAN *up;
+ struct usb_endpoint_descriptor *ep;
+
+ ret = -EINVAL;
+ udev = interface_to_usbdev(intf);
+ if (!udev)
+ goto err;
+
+ dev_dbg(&udev->dev, "%s\n", __func__);
+
+ // check if the interface is sane
+ ret = -EINVAL;
+ iface_desc = intf->cur_altsetting;
+ if (!iface_desc)
+ goto err;
+
+ if (iface_desc->desc.bInterfaceClass!=UCAN_CLASS)
+ return 0;
+
+ if (iface_desc->desc.bInterfaceSubClass!=UCAN_SUBCLASS)
+ return 0;
+
+ if (iface_desc->desc.bInterfaceProtocol!=0)
+ return 0;
+
+ // Infvalid interface Settings
+ if (iface_desc->desc.bNumEndpoints!=3)
+ goto err;
+
+ dev_info(&udev->dev, "Found USB CAN Class on Interface #%d\n", iface_desc->desc.iInterface);
+
+ // allocate driver resources
+ ret = -ENOMEM;
+ netdev = alloc_candev(sizeof(struct uCAN), MAX_TX_URBS);
+ if (!netdev)
+ goto err;
+ up = netdev_priv(netdev);
+
+ // get interface descriptors
+ for (i=0; i<iface_desc->desc.bNumEndpoints; i++) {
+ ep = &iface_desc->endpoint[i].desc;
+
+ if ( ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0 ) &&
+ ( (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK ) ) {
+ /* In Endpoint */
+ up->in_ep = ep;
+ }
+ else if ( ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 0) &&
+ ( (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK ) ) {
+ /* Out Endpoint */
+ up->out_ep = ep;
+ }
+ else if ( ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0) &&
+ ( (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_INT ) ) {
+ /* Out Endpoint */
+ up->irq_ep = ep;
+ }
+ }
+
+ // check if all interfaces are sane
+ ret = -EINVAL;
+ if ( (!up->in_ep) || (!up->out_ep) || (!up->irq_ep) ) {
+ dev_err(&udev->dev, "Invalid Endpoint Configuration\n");
+ goto err_free_candev;
+ }
+
+ if (up->in_ep->wMaxPacketSize < sizeof(struct uCAN_message_in) )
+ goto err_free_candev;
+ if (up->out_ep->wMaxPacketSize < sizeof(struct uCAN_message_out) )
+ goto err_free_candev;
+ if (up->irq_ep->wMaxPacketSize < sizeof(u32) )
+ goto err_free_candev;
+
+ dev_info(&udev->dev, " using EP %02x for input with max packet size #0x%x\n",
+ up->in_ep->bEndpointAddress, up->in_ep->wMaxPacketSize);
+ dev_info(&udev->dev, " using EP %02x for output with max packet size #0x%x\n",
+ up->out_ep->bEndpointAddress, up->out_ep->wMaxPacketSize);
+ dev_info(&udev->dev, " using EP %02x for irq input with max packet size #0x%x\n",
+ up->irq_ep->bEndpointAddress, up->irq_ep->wMaxPacketSize);
+
+ // initialze data
+ up->udev = udev;
+ up->netdev = netdev;
+
+ up->can.state = CAN_STATE_STOPPED;
+ up->can.bittiming_const = &up->device_info.bittiming_const;
+ up->can.do_set_bittiming = uCAN_set_bittiming;
+#ifdef CONFIG_CAN_HWFILTER
+ up->can.do_set_hwfilter = uCAN_set_hwfilter;
+#endif
+ netdev->netdev_ops = &uCAN_netdev_ops;
+
+ usb_set_intfdata(intf, up);
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ // allocate memory for cammand messages
+ up->tx_msg_buffer = devm_kzalloc(&udev->dev, up->out_ep->wMaxPacketSize, GFP_KERNEL);
+ if (!up->tx_msg_buffer)
+ goto err_free_candev;
+ up->rx_msg_buffer = devm_kzalloc(&udev->dev, up->in_ep->wMaxPacketSize, GFP_KERNEL);
+ if (!up->rx_msg_buffer)
+ goto err_free_candev;
+
+ // reset the device
+ ret = uCAN_command(up, UCAN_COMMAND_RESET, 0, 0);
+ if (ret)
+ goto err_free_candev;
+
+ // gather device information
+ ret = uCAN_get_device_info(up);
+ if (ret)
+ goto err_free_candev;
+
+#ifdef CONFIG_CAN_HWFILTER
+ // disable hardware filter by default
+ ret = uCAN_command(up, UCAN_COMMAND_FILTER, UCAN_FILTER_CLEAR, 0);
+ if (ret)
+ goto err_free_candev;
+
+ ret = uCAN_command(up, UCAN_COMMAND_FILTER, UCAN_FILTER_DISABLE, 0);
+ if (ret)
+ goto err_free_candev;
+#endif
+
+ dev_info(&up->udev->dev,
+ "Device Reports:\n"
+ " Frequency [Hz] : %d\n"
+ " TX Fifo [length] : %d\n"
+ " Time Segment 1 [min,max] : %d - %d\n"
+ " Time Segment 2 [min,max] : %d - %d\n"
+ " SWJ [max] : %d\n"
+ " Prescale [min-max,step] : %d - %d, %d\n"
+#ifdef CONFIG_CAN_HWFILTER
+ " Hardware filter banks : %d\n"
+ " Receive mailboxes : %d\n"
+#endif
+ " Supported modes :%s%s%s%s%s [%x]\n",
+ up->can.clock.freq,
+ up->device_info.tx_fifo,
+ up->can.bittiming_const->tseg1_min,
+ up->can.bittiming_const->tseg1_max,
+ up->can.bittiming_const->tseg2_min,
+ up->can.bittiming_const->tseg2_max,
+ up->can.bittiming_const->sjw_max,
+ up->can.bittiming_const->brp_min,
+ up->can.bittiming_const->brp_max,
+ up->can.bittiming_const->brp_inc,
+#ifdef CONFIG_CAN_HWFILTER
+ up->can.hwfilterbanks,
+ up->can.rxmailboxes,
+#endif
+ (up->can.ctrlmode_supported&CAN_CTRLMODE_LOOPBACK)?" Loopback":"",
+ (up->can.ctrlmode_supported&CAN_CTRLMODE_LISTENONLY)?" Silent":"",
+ (up->can.ctrlmode_supported&CAN_CTRLMODE_3_SAMPLES)?" 3-Sampling":"",
+ (up->can.ctrlmode_supported&CAN_CTRLMODE_ONE_SHOT)?" OneShot":"",
+ (up->can.ctrlmode_supported&CAN_CTRLMODE_BERR_REPORTING)?" BusErrReport":"",
+ up->can.ctrlmode_supported);
+
+ atomic_set(&up->active_tx_urbs, 0);
+ for (i=0; i<MAX_TX_URBS; i++)
+ up->tx_urb_contexts[i].echo_index=-1;
+
+ init_usb_anchor(&up->rx_urbs);
+ init_usb_anchor(&up->tx_urbs);
+
+ // register the device
+ ret = register_candev(netdev);
+ if (ret)
+ goto err_free_candev;
+
+ return 0;
+err_free_candev:
+ free_candev(netdev);
+err:
+ return ret;
+}
+
+/* disconnect the device */
+static void uCAN_disconnect(struct usb_interface *intf)
+{
+ struct usb_device *udev;
+ struct uCAN *up = usb_get_intfdata(intf);
+
+ udev = interface_to_usbdev(intf);
+ dev_dbg(&udev->dev, "%s\n", __func__);
+
+ usb_set_intfdata(intf, NULL);
+
+ if (up) {
+ unregister_netdev(up->netdev);
+ free_candev(up->netdev);
+ }
+}
+
+/* driver callbacks */
+static struct usb_driver uCAN_driver = {
+ .name = "uCAN",
+ .probe = uCAN_probe,
+ .disconnect = uCAN_disconnect,
+ .id_table = uCAN_table,
+};
+
+module_usb_driver(uCAN_driver);
+
diff --git a/drivers/net/can/usb/ucan.h b/drivers/net/can/usb/ucan.h
new file mode 100644
index 000000000000..dfe33088e405
--- /dev/null
+++ b/drivers/net/can/usb/ucan.h
@@ -0,0 +1,216 @@
+#ifndef __UCAN_H__
+
+/*
+ * Header file for CAN driver for uCAN
+ *
+ * Copyright (C) 2015 Theobroma Systems Design und Consulting GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * This driver is inspired by the 4.0.0 version of drivers/net/can/usb/ems_usb.c
+ *
+ */
+
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+
+#define UCAN_CLASS 0xA0
+#define UCAN_SUBCLASS 0x00
+
+/* 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 endianess
+ *
+ * INTR Endpoint: a single uint32_t storing the current space in the fifo
+ *
+ * OUT Enpoint: 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
+ */
+
+// UCAN Commands
+enum {
+ UCAN_COMMAND_START = 0,
+ UCAN_COMMAND_STOP = 1,
+ UCAN_COMMAND_SLEEP = 2,
+ UCAN_COMMAND_WAKEUP = 3,
+ UCAN_COMMAND_RESET = 4,
+ UCAN_COMMAND_GET = 5,
+ UCAN_COMMAND_GET_INFO = 0,
+ UCAN_COMMAND_FILTER = 6,
+ UCAN_FILTER_CLEAR = 0,
+ UCAN_FILTER_DISABLE = 1,
+};
+
+enum {
+ UCAN_MODE_LOOPBACK = (1<<0),
+ UCAN_MODE_SILENT = (1<<1),
+ UCAN_MODE_3_SAMPLES = (1<<2),
+ UCAN_MODE_ONE_SHOT = (1<<3),
+ UCAN_MODE_BERR_REPORT = (1<<4),
+};
+
+#define UCAN_OUT_COMMAND 0
+#define UCAN_OUT_SET_BITTIMING 1
+#define UCAN_OUT_TX 2
+#define UCAN_OUT_ENABLE_FILTER 3
+
+/* OUT Enpoint, outbound messages */
+struct uCAN_message_out {
+ u16 len; /* Length of the content include header */
+ u16 type; /* UCAN_OUT_COMMAND and friends */
+ union {
+ /***************************************************
+ * Device Command
+ * (type = UCAN_OUT_SET_BITTIMING)
+ ***************************************************/
+ struct {
+ u8 cmd; /* UCAN_COMMAND_START and friends */
+ u8 subcmd;
+ u16 val;
+ } __attribute__((packed)) command;
+
+ /***************************************************
+ * Set Bittiming
+ * (type = UCAN_OUT_SET_BITTIMING)
+ ***************************************************/
+ struct {
+ u32 tq; /* Time quanta (TQ) in nanoseconds */
+ u16 brp; /* TQ Prescaler */
+ u16 sample_point; /* Samplepoint on tenth percent */
+ u8 prop_seg; /* Propagation segment in TQs */
+ u8 phase_seg1; /* Phase buffer segment 1 in TQs */
+ u8 phase_seg2; /* Phase buffer segment 2 in TQs */
+ u8 sjw; /* Synchronisation jump width in TQs */
+ } __attribute__((packed)) bittiming;
+
+ /***************************************************
+ * Transmit CAN frame
+ * (type = UCAN_TX) && ((msg.can_msg.id & CAN_RTR_FLAG) == 0)
+ ***************************************************/
+ struct { /* note DLC is computed by
+ * msg.len - sizeof (msg.len)
+ * - sizeof (msg.type)
+ * - sizeof (msg.can_msg.id); */
+ u32 id;
+ u8 data[8]; // ensure data aligment to 4, by moving dlc after data
+ } __attribute__((packed)) can_msg;
+
+ /***************************************************
+ * Transmit RTR CAN frame
+ * (type = UCAN_TX) && ((msg.can_msg.id & CAN_RTR_FLAG) != 0)
+ ***************************************************/
+ struct {
+ u32 id;
+ u8 dlc;
+ } __attribute__((packed)) can_rtr_msg;
+
+ /***************************************************
+ * Enable Filter
+ * (type = UCAN_OUT_ENABLE_FILTER)
+ ***************************************************/
+ struct {
+ u32 id;
+ u32 mask;
+ u16 mbox;
+ } enable_filter;
+ } __attribute__((aligned(0x4))) msg;
+} __attribute__((packed));
+
+#define UCAN_IN_DEVICE_INFO 0
+#define UCAN_IN_RX 2
+
+/* IN Enpoint, inbound messages */
+struct uCAN_message_in {
+ u16 len; /* Length of the content include header */
+ u16 type; /* UCAN_IN_DEVICE_INFO and friends */
+
+ union {
+ /***************************************************
+ * Device Information
+ * (type = UCAN_IN_DEVICE_INFO)
+ ***************************************************/
+ struct {
+ u32 freq; /* Clock Frequency for tq
+ * generation */
+ u8 tx_fifo; /* Size of the transmission
+ * fifo */
+ u8 sjw_max;
+ u8 tseg1_min;
+ u8 tseg1_max;
+ u8 tseg2_min;
+ u8 tseg2_max;
+ u16 brp_inc;
+ u32 brp_min;
+ u32 brp_max;
+ u16 ctrlmodes; /* supported control modes
+ * ors of UCAN_MODE_* */
+ u16 hwfilter; /* Number of HW filter
+ * banks */
+ u16 rxmboxes; /* Number Receive
+ * Mailboxes */
+ } __attribute__((packed)) device_info;
+
+ /***************************************************
+ * CAN RTR Frame received
+ * (type == UCAN_IN_RX) && ((msg.can_msg.id & CAN_RTR_FLAG) == 0)
+ ***************************************************/
+ struct { /* note DLC is computed by
+ * msg.len - sizeof (msg.len)
+ * - sizeof (msg.type)
+ * - sizeof (msg.can_msg.id); */
+ u32 id;
+ u8 data[8]; // ensure data aligment to 4, by moving dlc after data
+ } __attribute__((packed)) can_msg;
+
+ /***************************************************
+ * CAN RTR Frame received
+ * (type == UCAN_IN_RX) && ((msg.can_msg.id & CAN_RTR_FLAG) != 0)
+ ***************************************************/
+ struct {
+ u32 id;
+ u8 dlc;
+ } __attribute__((packed)) can_rtr_msg;
+ } __attribute__((aligned(0x4))) msg;
+} __attribute__((packed));
+
+
+/* Macros to calculate message lengths */
+#define UCAN_OUT_HDR_SIZE offsetof(struct uCAN_message_out, msg)
+#define UCAN_OUT_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member) )
+
+#define UCAN_IN_HDR_SIZE offsetof(struct uCAN_message_in, msg)
+#define UCAN_IN_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member) )
+
+#endif
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 735f9f8c4e43..e83ac15ea364 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -38,7 +38,11 @@ struct can_priv {
const struct can_bittiming_const *bittiming_const,
*data_bittiming_const;
struct can_clock clock;
-
+#ifdef CONFIG_CAN_HWFILTER
+ struct can_hwfilter filter;
+ int hwfilterbanks;
+ int rxmailboxes;
+#endif
enum can_state state;
u32 ctrlmode;
u32 ctrlmode_supported;
@@ -53,6 +57,9 @@ struct can_priv {
enum can_state *state);
int (*do_get_berr_counter)(const struct net_device *dev,
struct can_berr_counter *bec);
+#ifdef CONFIG_CAN_HWFILTER
+ int (*do_set_hwfilter)(struct net_device *dev);
+#endif
unsigned int echo_skb_max;
struct sk_buff **echo_skb;
diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h
index 94ffe0c83ce7..0456aac91d3e 100644
--- a/include/uapi/linux/can/netlink.h
+++ b/include/uapi/linux/can/netlink.h
@@ -20,6 +20,25 @@
#include <linux/types.h>
+#ifdef CONFIG_CAN_HWFILTER
+/*
+ * CAN hardware filter parameters
+ */
+
+#define CAN_MAX_FILTERS 32
+
+struct __can_hwfilter {
+ __u32 id;
+ __u32 mask;
+ __u16 mbox;
+};
+
+struct can_hwfilter {
+ __u32 count;
+ struct __can_hwfilter filter[CAN_MAX_FILTERS];
+};
+#endif
+
/*
* CAN bit-timing parameters
*
@@ -127,6 +146,9 @@ enum {
IFLA_CAN_BERR_COUNTER,
IFLA_CAN_DATA_BITTIMING,
IFLA_CAN_DATA_BITTIMING_CONST,
+#ifdef CONFIG_CAN_HWFILTER
+ IFLA_CAN_HARDWARE_FILTER,
+#endif
__IFLA_CAN_MAX
};