-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Matthias Fuchs wrote: > Hi, > > here comes my updated V3 patch. tx_context handling is now working fine > and the implementation makes much more sense. No more crashes. > This seems to be a good candidate.
looks quite good, just some nitpicking inline... Marc > > Matthias > > Changelog: > V3: > - handle msg->msg.txdone.status != 0 in esd_usb2_tx_done_msg() > - move active_tx_urbs-- to esd_usb2_tx_done_msg() from > ...write_bulk_callback > - move netif_wake_queue(netdev) to esd_usb2_tx_done_msg() > - add BTR macros > - rename active_tx_urbs into active_tx_jobs > > V2: > - remove duplicate line "netdev->flags |= IFF_ECHO; ..." > in esd_usb2_probe_one_net() > - set can.state back to CAN_STATE_STOPPED in esd_usb2_close() > Without this setting the bitrate via sysfs after an interface > up/down cycle does not work. > - remove "#undef DEBUG" line > - codingstyle clean (tabs, empty lines, ...) > - don't check against 0, but use !() > - remove netif_queue_stopped() check before calling netif_wake_queue() > - remove double netif_device_detach() from esd_usb2_open() > - print canbtr in esd_usb2_set_bittiming() > - use __types in structures that a exchanged with the device > - add ESD_MAX_ID_SEGMENT macro to get rid of the magic hardcoded "64" > when setting up the device's ID filter. > - Add some comments about the IDADD message. > - move esd_usb2_bittiming_const close to esd_usb2_set_bittiming() > - move macro definitions to top of file > - use if() for single case switch statements > - add macros for esd bus state event codes > - order declarations somehow: move structs to top > - get rid of ibuf variable in esd_usb2_read_bulk_callback() > - rename no_nets to net_count :-) > - consequently do not use unlikely() > - call can_get_echo_skb() from esd_usb2_tx_done_msg() to loopback > only when message has been sent successfully > - increase MAX_TX_URBS because releasing tx_contexts is now done > much later and we easily run out of free tx_contexts > - check dev->nets[i] before netif_device_detach() in ...read_bulk_callback() > - make esd_usb2_setup_rx_urbs() succeed when we got at least one urb > setup correctly > - implement common error handling in esd_usb2_start_xmit() > - handle failure of alloc_can_(err_)sbk: stats->rx_dropped++ > - rename netdev's private data pointer from "net" to "priv" > - add "device %s registered" message on successful net creation > > Index: Makefile > =================================================================== > --- Makefile (revision 1095) > +++ Makefile (working copy) > @@ -24,6 +24,7 @@ > export CONFIG_CAN_EMS_104M=m > export CONFIG_CAN_ESD_PCI=m > export CONFIG_CAN_ESD_331=m > +export CONFIG_CAN_ESD_USB2=m > export CONFIG_CAN_PIPCAN=m > export CONFIG_CAN_SOFTING=m > export CONFIG_CAN_SOFTING_CS=m > Index: drivers/net/can/usb/Kconfig > =================================================================== > --- drivers/net/can/usb/Kconfig (revision 1095) > +++ drivers/net/can/usb/Kconfig (working copy) > @@ -7,4 +7,10 @@ > This driver is for the one channel CPC-USB/ARM7 CAN/USB interface > from from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). > > +config CAN_ESD_USB2 > + tristate "ESD USB/2 CAN/USB interface" > + ---help--- > + This driver supports the CAN-USB/2 interface > + from esd electronic system design gmbh (http://www.esd.eu). > + > endmenu > Index: drivers/net/can/usb/esd_usb2.c > =================================================================== > --- drivers/net/can/usb/esd_usb2.c (revision 0) > +++ drivers/net/can/usb/esd_usb2.c (revision 0) > @@ -0,0 +1,1144 @@ > +/* > + * CAN driver for esd CAN-USB/2 > + * > + * Copyright (C) 2010 Matthias Fuchs <[email protected]>, esd 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; if not, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > + */ > +#include <linux/init.h> > +#include <linux/signal.h> > +#include <linux/slab.h> > +#include <linux/module.h> > +#include <linux/netdevice.h> > +#include <linux/usb.h> > + > +#include <socketcan/can.h> > +#include <socketcan/can/dev.h> > +#include <socketcan/can/error.h> > + > +MODULE_AUTHOR("Matthias Fuchs <[email protected]>"); > +MODULE_DESCRIPTION("CAN driver for esd CAN-USB/2 interfaces"); > +MODULE_LICENSE("GPL v2"); > + > +/* Define these values to match your devices */ > +#define USB_ESDGMBH_VENDOR_ID 0x0ab4 > +#define USB_CANUSB2_PRODUCT_ID 0x0010 > + > +#define ESD_USB2_CAN_CLOCK 60000000 > +#define ESD_USB2_MAX_NETS 2 > + > +/* USB2 commands */ > +#define CMD_VERSION 1 /* also used for VERSION_REPLY */ > +#define CMD_CAN_RX 2 /* device to host only */ > +#define CMD_CAN_TX 3 /* also used for TX_DONE */ > +#define CMD_SETBAUD 4 /* also used for SETBAUD_REPLY */ > +#define CMD_TS 5 /* also used for TS_REPLY */ > +#define CMD_IDADD 6 /* also used for IDADD_REPLY */ > + > +/* esd CAN message flags - dlc field */ > +#define ESD_RTR 0x10 > + > +/* esd CAN message flags - id field */ > +#define ESD_EXTID 0x20000000 > +#define ESD_EVENT 0x40000000 > +#define ESD_IDMASK 0x1fffffff > + > +/* esd CAN event ids used by this driver */ > +#define ESD_EV_CAN_ERROR_EXT 2 > + > +/* baudrate message flags */ > +#define ESD_USB2_UBR 0x80000000 > +#define ESD_USB2_LOM 0x40000000 > +#define ESD_USB2_NO_BAUDRATE 0x7fffffff > +#define ESD_USB2_TSEG1_MIN 1 > +#define ESD_USB2_TSEG1_MAX 16 > +#define ESD_USB2_TSEG1_SHIFT 16 > +#define ESD_USB2_TSEG2_MIN 1 > +#define ESD_USB2_TSEG2_MAX 8 > +#define ESD_USB2_TSEG2_SHIFT 20 > +#define ESD_USB2_SJW_MAX 4 > +#define ESD_USB2_SJW_SHIFT 14 > +#define ESD_USB2_BRP_MIN 1 > +#define ESD_USB2_BRP_MAX 1024 > +#define ESD_USB2_BRP_INC 1 > +#define ESD_USB2_3_SAMPLES 0x00800000 > + > +/* esd IDADD message */ > +#define ESD_ID_ENABLE 0x80 > +#define ESD_MAX_ID_SEGMENT 64 > + > +/* SJA1000 ECC register (emulated by usb2 firmware) */ > +#define SJA1000_ECC_SEG 0x1F > +#define SJA1000_ECC_DIR 0x20 > +#define SJA1000_ECC_ERR 0x06 > +#define SJA1000_ECC_BIT 0x00 > +#define SJA1000_ECC_FORM 0x40 > +#define SJA1000_ECC_STUFF 0x80 > +#define SJA1000_ECC_MASK 0xc0 > + > +/* esd bus state event codes */ > +#define ESD_BUSSTATE_MASK 0xc0 > +#define ESD_BUSSTATE_WARN 0x40 > +#define ESD_BUSSTATE_ERRPASSIVE 0x80 > +#define ESD_BUSSTATE_BUSOFF 0xc0 > + > +#define RX_BUFFER_SIZE 1024 > +#define MAX_RX_URBS 4 > +#define MAX_TX_URBS 10 > + > +struct header_msg { > + __u8 len; /* len is always the total message length in 32bit words */ > + __u8 cmd; > + __u8 rsvd[2]; > +}; > + > +struct version_msg { > + __u8 len; > + __u8 cmd; > + __u8 rsvd; > + __u8 flags; > + __le32 drv_version; > +}; > + > +struct version_reply_msg { > + __u8 len; > + __u8 cmd; > + __u8 nets; > + __u8 features; > + __le32 version; > + __u8 name[16]; > + __le32 rsvd; > + __le32 ts; > +}; > + > +struct rx_msg { > + __u8 len; > + __u8 cmd; > + __u8 net; > + __u8 dlc; > + __le32 ts; > + __le32 id; /* upper 3 bits contain flags */ > + __u8 data[8]; > +}; > + > +struct tx_msg { > + __u8 len; > + __u8 cmd; > + __u8 net; > + __u8 dlc; > + __le32 hnd; > + __le32 id; /* upper 3 bits contain flags */ > + __u8 data[8]; > +}; > + > +struct tx_done_msg { > + __u8 len; > + __u8 cmd; > + __u8 net; > + __u8 status; > + __le32 hnd; > + __le32 ts; > +}; > + > +struct id_filter_msg { > + __u8 len; > + __u8 cmd; > + __u8 net; > + __u8 option; > + __le32 mask[65]; > +}; > + > +struct id_filter_reply_msg { > + __u8 len; > + __u8 cmd; > + __u8 net; > + __u8 option; > + __le16 added; > + __le16 removed; > + __le16 error; > + __le16 active; > +}; > + > +struct set_baudrate_msg { > + __u8 len; > + __u8 cmd; > + __u8 net; > + __u8 rsvd; > + __le32 baud; > +}; > + > +struct set_baudrate_reply_msg { > + __u8 len; > + __u8 cmd; > + __u8 net; > + __u8 rsvd; > + __le32 baud; > + __le32 ts; > +}; > + > +/* Main message type used between library and application */ > +struct __attribute__ ((packed)) esd_usb2_msg { > + union { > + struct header_msg hdr; > + struct version_msg version; > + struct version_reply_msg version_reply; > + struct rx_msg rx; > + struct tx_msg tx; > + struct tx_done_msg txdone; > + struct set_baudrate_msg setbaud; > + struct set_baudrate_reply_msg setbaud_reply; > + struct id_filter_msg filter; > + struct id_filter_reply_msg filter_reply; > + } msg; > +}; > + > +static struct usb_device_id esd_usb2_table[] = { > + {USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSB2_PRODUCT_ID)}, > + {} > +}; > +MODULE_DEVICE_TABLE(usb, esd_usb2_table); > + > +struct esd_usb2_net_priv; > + > +struct esd_tx_urb_context { > + struct esd_usb2_net_priv *priv; > + u32 echo_index; > + int dlc; > +}; > + > +struct esd_usb2 { > + struct usb_device *udev; > + struct esd_usb2_net_priv *nets[ESD_USB2_MAX_NETS]; > + > + struct usb_anchor rx_submitted; > + > + int net_count; > + u32 version; > + int rxinitdone; > +}; > + > +struct esd_usb2_net_priv { > + struct can_priv can; /* must be the first member */ > + > + atomic_t active_tx_jobs; > + struct usb_anchor tx_submitted; > + struct esd_tx_urb_context tx_contexts[MAX_TX_URBS]; > + > + int open_time; > + struct esd_usb2 *usb2; > + struct net_device *netdev; > + int index; > + u8 old_state; > +}; > + > +static void esd_usb2_rx_event(struct esd_usb2_net_priv *priv, > + struct esd_usb2_msg *msg) > +{ > + struct net_device_stats *stats = &priv->netdev->stats; > + struct can_frame *cf; > + struct sk_buff *skb; > + u32 id = le32_to_cpu(msg->msg.rx.id) & ESD_IDMASK; > + > + if (id == ESD_EV_CAN_ERROR_EXT) { > + u8 state = msg->msg.rx.data[0]; > + u8 ecc = msg->msg.rx.data[1]; > + u8 txerr = msg->msg.rx.data[2]; > + u8 rxerr = msg->msg.rx.data[3]; > + > + skb = alloc_can_err_skb(priv->netdev, &cf); > + if (skb == NULL) { > + stats->rx_dropped++; > + return; > + } > + > + if (state != priv->old_state) { > + priv->old_state = state; > + > + switch (state & ESD_BUSSTATE_MASK) { > + case ESD_BUSSTATE_BUSOFF: > + priv->can.state = CAN_STATE_BUS_OFF; > + cf->can_id |= CAN_ERR_BUSOFF; > + can_bus_off(priv->netdev); > + break; > + case ESD_BUSSTATE_WARN: > + priv->can.state = CAN_STATE_ERROR_WARNING; > + priv->can.can_stats.error_warning++; > + break; > + case ESD_BUSSTATE_ERRPASSIVE: > + priv->can.state = CAN_STATE_ERROR_PASSIVE; > + priv->can.can_stats.error_passive++; > + break; > + default: > + priv->can.state = CAN_STATE_ERROR_ACTIVE; > + break; > + } > + } else { > + priv->can.can_stats.bus_error++; > + stats->rx_errors++; > + > + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; > + > + switch (ecc & SJA1000_ECC_MASK) { > + case SJA1000_ECC_BIT: > + cf->data[2] |= CAN_ERR_PROT_BIT; > + break; > + case SJA1000_ECC_FORM: > + cf->data[2] |= CAN_ERR_PROT_FORM; > + break; > + case SJA1000_ECC_STUFF: > + cf->data[2] |= CAN_ERR_PROT_STUFF; > + break; > + default: > + cf->data[2] |= CAN_ERR_PROT_UNSPEC; > + cf->data[3] = ecc & SJA1000_ECC_SEG; > + break; > + } > + > + /* Error occured during transmission? */ > + if (!(ecc & SJA1000_ECC_DIR)) > + cf->data[2] |= CAN_ERR_PROT_TX; > + > + if (priv->can.state == CAN_STATE_ERROR_WARNING || > + priv->can.state == CAN_STATE_ERROR_PASSIVE) { > + cf->data[1] = (txerr > rxerr) ? > + CAN_ERR_CRTL_TX_PASSIVE : > + CAN_ERR_CRTL_RX_PASSIVE; > + } > + } > + > + netif_rx(skb); > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) > + priv->netdev->last_rx = jiffies; > +#endif > + stats->rx_packets++; > + stats->rx_bytes += cf->can_dlc; > + } > +} > + > +static void esd_usb2_rx_can_msg(struct esd_usb2_net_priv *priv, > + struct esd_usb2_msg *msg) > +{ > + struct net_device_stats *stats = &priv->netdev->stats; > + struct can_frame *cf; > + struct sk_buff *skb; > + int i; > + u32 id; > + > + if (!netif_device_present(priv->netdev)) > + return; > + > + id = le32_to_cpu(msg->msg.rx.id); > + > + if (id & ESD_EVENT) { > + esd_usb2_rx_event(priv, msg); > + } else { > + skb = alloc_can_skb(priv->netdev, &cf); > + if (skb == NULL) { > + stats->rx_dropped++; > + return; > + } > + > + cf->can_id = id & ESD_IDMASK; > + cf->can_dlc = get_can_dlc(msg->msg.rx.dlc); > + > + if (id & ESD_EXTID) > + cf->can_id |= CAN_EFF_FLAG; > + > + if (msg->msg.rx.dlc & ESD_RTR) { > + cf->can_id |= CAN_RTR_FLAG; > + } else { > + for (i = 0; i < cf->can_dlc; i++) > + cf->data[i] = msg->msg.rx.data[i]; > + } > + > + netif_rx(skb); > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) > + priv->netdev->last_rx = jiffies; > +#endif > + stats->rx_packets++; > + stats->rx_bytes += cf->can_dlc; > + } > + > + return; > +} > + > +static void esd_usb2_tx_done_msg(struct esd_usb2_net_priv *priv, > + struct esd_usb2_msg *msg) > +{ > + struct net_device_stats *stats = &priv->netdev->stats; > + struct net_device *netdev = priv->netdev; > + struct esd_tx_urb_context *context; > + int done_index; > + > + if (!netif_device_present(netdev)) > + return; > + > + done_index = msg->msg.txdone.hnd & ~0x80000000; > + context = &priv->tx_contexts[done_index]; How far do you trust your hardware? I mean what about doing some out of bounds checking for the tx_contexts array? If MAX_TX_URBS is a power of two you can adjust the mask here.... > + > + if (!msg->msg.txdone.status) { > + stats->tx_packets++; > + stats->tx_bytes += context->dlc; > + can_get_echo_skb(netdev, context->echo_index); > + } else { > + stats->tx_errors++; > + can_free_echo_skb(netdev, context->echo_index); > + } > + > + /* Release context */ > + context->echo_index = MAX_TX_URBS; > + atomic_dec(&priv->active_tx_jobs); > + > + netif_wake_queue(netdev); > +} > + > +static void esd_usb2_read_bulk_callback(struct urb *urb) > +{ > + struct esd_usb2 *dev = urb->context; > + int retval; > + int pos = 0; > + int i; > + > + switch (urb->status) { > + case 0: /* success */ > + break; > + > + case -ENOENT: > + case -ESHUTDOWN: > + return; > + > + default: > + dev_info(dev->udev->dev.parent, > + "Rx URB aborted (%d)\n", urb->status); > + goto resubmit_urb; > + } > + > + while (pos < urb->actual_length) { > + struct esd_usb2_msg *msg; > + > + msg = (struct esd_usb2_msg *)(urb->transfer_buffer + pos); > + > + switch (msg->msg.hdr.cmd) { > + case CMD_CAN_RX: > + esd_usb2_rx_can_msg(dev->nets[msg->msg.rx.net], msg); > + break; > + > + case CMD_CAN_TX: > + esd_usb2_tx_done_msg(dev->nets[msg->msg.txdone.net], > + msg); > + break; > + } > + > + pos += msg->msg.hdr.len << 2; > + > + if (pos > urb->actual_length) { > + dev_err(dev->udev->dev.parent, "format error\n"); > + break; > + } > + } > + > +resubmit_urb: > + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), > + urb->transfer_buffer, RX_BUFFER_SIZE, > + esd_usb2_read_bulk_callback, dev); > + > + retval = usb_submit_urb(urb, GFP_ATOMIC); > + if (retval == -ENODEV) { > + for (i = 0; i < dev->net_count; i++) { > + if (dev->nets[i]) > + netif_device_detach(dev->nets[i]->netdev); > + } > + } else if (retval) { > + dev_err(dev->udev->dev.parent, > + "failed resubmitting read bulk urb: %d\n", retval); > + } > + > + return; > +} > + > +/* > + * callback for bulk IN urb > + */ > +static void esd_usb2_write_bulk_callback(struct urb *urb) > +{ > + struct esd_tx_urb_context *context = urb->context; > + struct esd_usb2_net_priv *priv; > + struct esd_usb2 *dev; > + struct net_device *netdev; > + size_t size = sizeof(struct esd_usb2_msg); > + > + BUG_ON(!context); > + > + priv = context->priv; > + netdev = priv->netdev; > + dev = priv->usb2; > + > + /* free up our allocated buffer */ > + usb_buffer_free(urb->dev, size, > + urb->transfer_buffer, urb->transfer_dma); > + > + if (!netif_device_present(netdev)) > + return; > + > + if (urb->status) > + dev_info(ND2D(netdev), "Tx URB aborted (%d)\n", > + urb->status); > + > + netdev->trans_start = jiffies; > +} > + > +#ifdef CONFIG_SYSFS > +static ssize_t show_firmware(struct device *d, > + struct device_attribute *attr, char *buf) > +{ > + struct usb_interface *intf = to_usb_interface(d); > + struct esd_usb2 *dev = usb_get_intfdata(intf); > + > + return sprintf(buf, "%d.%d.%d\n", > + (dev->version >> 12) & 0xf, > + (dev->version >> 8) & 0xf, > + dev->version & 0xff); > +} > +static DEVICE_ATTR(firmware, S_IRUGO, show_firmware, NULL); > + > +static ssize_t show_hardware(struct device *d, > + struct device_attribute *attr, char *buf) > +{ > + struct usb_interface *intf = to_usb_interface(d); > + struct esd_usb2 *dev = usb_get_intfdata(intf); > + > + return sprintf(buf, "%d.%d.%d\n", > + (dev->version >> 28) & 0xf, > + (dev->version >> 24) & 0xf, > + (dev->version >> 16) & 0xff); > +} > +static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL); > + > +static ssize_t show_nets(struct device *d, > + struct device_attribute *attr, char *buf) > +{ > + struct usb_interface *intf = to_usb_interface(d); > + struct esd_usb2 *dev = usb_get_intfdata(intf); > + > + return sprintf(buf, "%d", dev->net_count); > +} > +static DEVICE_ATTR(nets, S_IRUGO, show_nets, NULL); > +#endif > + > +static int esd_usb2_send_msg(struct esd_usb2 *dev, struct esd_usb2_msg *msg) > +{ > + int actual_length; > + > + return usb_bulk_msg(dev->udev, > + usb_sndbulkpipe(dev->udev, 2), > + msg, > + msg->msg.hdr.len << 2, > + &actual_length, > + 1000); > +} > + > +static int esd_usb2_wait_msg(struct esd_usb2 *dev, > + struct esd_usb2_msg *msg) > +{ > + int actual_length; > + > + return usb_bulk_msg(dev->udev, > + usb_rcvbulkpipe(dev->udev, 1), > + msg, > + sizeof(*msg), > + &actual_length, > + 1000); > +} > + > +static int esd_usb2_setup_rx_urbs(struct esd_usb2 *dev) > +{ > + int i, err = 0; > + > + if (dev->rxinitdone) > + return 0; > + > + for (i = 0; i < MAX_RX_URBS; i++) { > + struct urb *urb = NULL; > + u8 *buf = NULL; > + > + /* create a URB, and a buffer for it */ > + urb = usb_alloc_urb(0, GFP_KERNEL); > + if (!urb) { > + dev_warn(dev->udev->dev.parent, > + "No memory left for URBs\n"); > + err = -ENOMEM; > + break; > + } > + > + buf = usb_buffer_alloc(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, > + &urb->transfer_dma); > + if (!buf) { > + dev_warn(dev->udev->dev.parent, > + "No memory left for USB buffer\n"); > + err = -ENOMEM; > + goto freeurb; > + } > + > + usb_fill_bulk_urb(urb, dev->udev, > + usb_rcvbulkpipe(dev->udev, 1), > + buf, RX_BUFFER_SIZE, > + esd_usb2_read_bulk_callback, dev); > + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; > + usb_anchor_urb(urb, &dev->rx_submitted); > + > + err = usb_submit_urb(urb, GFP_KERNEL); > + if (err) { > + usb_unanchor_urb(urb); > + usb_buffer_free(dev->udev, RX_BUFFER_SIZE, buf, > + urb->transfer_dma); > + } > + > + freeurb: > + /* Drop reference, USB core will take care of freeing it */ > + usb_free_urb(urb); > + if (err) > + break; > + } > + > + /* Did we submit any URBs */ > + if (i == 0) { > + dev_err(dev->udev->dev.parent, "couldn't setup read URBs\n"); > + return err; > + } > + > + /* Warn if we've couldn't transmit all the URBs */ > + if (i < MAX_RX_URBS) { > + dev_warn(dev->udev->dev.parent, > + "rx performance may be slow\n"); > + } > + > + dev->rxinitdone = 1; > + return 0; > +} > + > +/* > + * Start interface > + */ > +static int esd_usb2_start(struct esd_usb2_net_priv *priv) > +{ > + struct esd_usb2 *dev = priv->usb2; > + struct net_device *netdev = priv->netdev; > + struct esd_usb2_msg msg; > + int err, i; > + > + /* > + * enable all (!) IDs > + * The IDADD message takes up to ESD_MAX_ID_SEGMENT 32 bit > + * bitmasks = 2048 bits. A set bit enables reception > + * of the corresponding CAN identifier. > + * The next bitmask value following the CAN 2.0A > + * bits is used to enable reception of extended CAN frames. > + */ > + msg.msg.hdr.cmd = CMD_IDADD; > + msg.msg.hdr.len = 1 + ESD_MAX_ID_SEGMENT + 1; > + msg.msg.filter.net = priv->index; > + msg.msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */ > + for (i = 0; i < ESD_MAX_ID_SEGMENT; i++) > + msg.msg.filter.mask[i] = cpu_to_le32(0xffffffff); > + /* enable 29bit extended IDs */ > + msg.msg.filter.mask[ESD_MAX_ID_SEGMENT] = cpu_to_le32(0x00000001); I don't know the hardware, but how corresponds 0x1 to 29 bits? > + > + err = esd_usb2_send_msg(dev, &msg); > + if (err) > + goto failed; > + > + err = esd_usb2_setup_rx_urbs(dev); > + if (err) > + goto failed; > + > + priv->can.state = CAN_STATE_ERROR_ACTIVE; > + > + return 0; > + > +failed: > + if (err == -ENODEV) > + netif_device_detach(netdev); > + > + dev_err(ND2D(netdev), "couldn't start device: %d\n", err); > + > + return err; > +} > + > +static void unlink_all_urbs(struct esd_usb2 *dev) > +{ > + struct esd_usb2_net_priv *priv; > + int i; > + > + usb_kill_anchored_urbs(&dev->rx_submitted); > + for (i = 0; i < dev->net_count; i++) { > + priv = dev->nets[i]; > + if (priv) { > + usb_kill_anchored_urbs(&priv->tx_submitted); > + atomic_set(&priv->active_tx_jobs, 0); > + > + for (i = 0; i < MAX_TX_URBS; i++) > + priv->tx_contexts[i].echo_index = MAX_TX_URBS; > + } > + } > +} > + > +static int esd_usb2_open(struct net_device *netdev) > +{ > + struct esd_usb2_net_priv *priv = netdev_priv(netdev); > + int err; > + > + /* common open */ > + err = open_candev(netdev); > + if (err) > + return err; > + > + /* finally start device */ > + err = esd_usb2_start(priv); > + if (err) { > + dev_warn(ND2D(netdev), "couldn't start device: %d\n", err); > + > + close_candev(netdev); > + > + return err; > + } > + > + priv->open_time = jiffies; > + > + netif_start_queue(netdev); > + > + return 0; > +} > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) > +static int esd_usb2_start_xmit(struct sk_buff *skb, struct net_device > *netdev) > +#else > +static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb, > + struct net_device *netdev) > +#endif > +{ > + struct esd_usb2_net_priv *priv = netdev_priv(netdev); > + struct esd_usb2 *dev = priv->usb2; > + struct esd_tx_urb_context *context = NULL; > + struct net_device_stats *stats = &netdev->stats; > + struct can_frame *cf = (struct can_frame *)skb->data; > + struct esd_usb2_msg *msg; > + struct urb *urb; > + u8 *buf; > + int i, err; > + int ret = NETDEV_TX_OK; > + size_t size = sizeof(struct esd_usb2_msg); > + > + /* create a URB, and a buffer for it, and copy the data to the URB */ > + urb = usb_alloc_urb(0, GFP_ATOMIC); > + if (!urb) { > + dev_err(ND2D(netdev), "No memory left for URBs\n"); > + goto nourbmem; > + } > + > + buf = usb_buffer_alloc(dev->udev, size, GFP_ATOMIC, &urb->transfer_dma); > + if (!buf) { > + dev_err(ND2D(netdev), "No memory left for USB buffer\n"); > + goto nobufmem; > + } > + > + msg = (struct esd_usb2_msg *)buf; > + > + msg->msg.hdr.len = 3; /* minimal length */ > + msg->msg.hdr.cmd = CMD_CAN_TX; > + msg->msg.tx.net = priv->index; > + msg->msg.tx.dlc = cf->can_dlc; > + msg->msg.tx.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK); > + > + if (cf->can_id & CAN_RTR_FLAG) > + msg->msg.tx.dlc |= ESD_RTR; > + > + if (cf->can_id & CAN_EFF_FLAG) > + msg->msg.tx.id |= cpu_to_le32(ESD_EXTID); > + > + for (i = 0; i < cf->can_dlc; i++) > + msg->msg.tx.data[i] = cf->data[i]; > + > + msg->msg.hdr.len += (cf->can_dlc + 3) >> 2; > + > + for (i = 0; i < MAX_TX_URBS; i++) { > + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { > + context = &priv->tx_contexts[i]; > + break; > + } > + } > + > + /* > + * This may never happen. > + */ > + if (!context) { > + dev_warn(ND2D(netdev), "couldn't find free context\n"); > +o ret = NETDEV_TX_BUSY; > + goto releasebuf; > + } > + > + context->priv = priv; > + context->echo_index = i; > + context->dlc = cf->can_dlc; > + > + /* hnd must not be 0 */ > + msg->msg.tx.hnd = 0x80000000 | i; /* returned in TX done message */ > + > + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf, > + msg->msg.hdr.len << 2, > + esd_usb2_write_bulk_callback, context); > + > + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; > + > + usb_anchor_urb(urb, &priv->tx_submitted); > + > + can_put_echo_skb(skb, netdev, context->echo_index); > + > + atomic_inc(&priv->active_tx_jobs); > + > + err = usb_submit_urb(urb, GFP_ATOMIC); > + if (err) { > + can_free_echo_skb(netdev, context->echo_index); > + > + atomic_dec(&priv->active_tx_jobs); > + usb_unanchor_urb(urb); > + > + if (err == -ENODEV) > + netif_device_detach(netdev); > + else > + dev_warn(ND2D(netdev), "failed tx_urb %d\n", err); > + > + goto releasebuf; > + } else { > + netdev->trans_start = jiffies; > + > + /* Slow down tx path */ > + if (atomic_read(&priv->active_tx_jobs) >= MAX_TX_URBS) > + netif_stop_queue(netdev); > + } > + > + /* > + * Release our reference to this URB, the USB core will eventually free > + * it entirely. > + */ > + usb_free_urb(urb); > + > + return NETDEV_TX_OK; > + > +releasebuf: > + usb_buffer_free(dev->udev, size, buf, urb->transfer_dma); > + > +nobufmem: > + usb_free_urb(urb); > + > +nourbmem: > + if (ret != NETDEV_TX_BUSY) { > + if (skb) > + dev_kfree_skb(skb); > + > + stats->tx_dropped++; > + } > + > + return ret; > +} > + > +static int esd_usb2_close(struct net_device *netdev) > +{ > + struct esd_usb2_net_priv *priv = netdev_priv(netdev); > + struct esd_usb2_msg msg; > + int i; > + > + /* Disable all IDs > + * The IDADD message takes up to ESD_MAX_ID_SEGMENT 32 bit > + * bitmasks = 2048 bits. A cleared bit disables reception > + * of the corresponding CAN identifier. > + * The next bitmask value following the CAN 2.0A > + * bits is used to disable reception of extended CAN frames > + * at all. > + */ nitpick: comments should look like this: /* * this is a * multiline comment */ > + msg.msg.hdr.cmd = CMD_IDADD; > + msg.msg.hdr.len = 1 + ESD_MAX_ID_SEGMENT + 1; > + msg.msg.filter.net = priv->index; > + msg.msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */ > + for (i = 0; i <= ESD_MAX_ID_SEGMENT; i++) > + msg.msg.filter.mask[i] = 0; > + esd_usb2_send_msg(priv->usb2, &msg); > + > + /* set CAN controller to reset mode */ > + msg.msg.hdr.len = 2; > + msg.msg.hdr.cmd = CMD_SETBAUD; > + msg.msg.setbaud.net = priv->index; > + msg.msg.setbaud.rsvd = 0; > + msg.msg.setbaud.baud = cpu_to_le32(ESD_USB2_NO_BAUDRATE); > + esd_usb2_send_msg(priv->usb2, &msg); > + > + priv->can.state = CAN_STATE_STOPPED; > + > + netif_stop_queue(netdev); > + > + close_candev(netdev); > + > + priv->open_time = 0; > + > + return 0; > +} > + > +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28) > +static const struct net_device_ops esd_usb2_netdev_ops = { > + .ndo_open = esd_usb2_open, > + .ndo_stop = esd_usb2_close, > + .ndo_start_xmit = esd_usb2_start_xmit, > +}; > +#endif > + > +static struct can_bittiming_const esd_usb2_bittiming_const = { > + .name = "esd_usb2", > + .tseg1_min = ESD_USB2_TSEG1_MIN, > + .tseg1_max = ESD_USB2_TSEG1_MAX, > + .tseg2_min = ESD_USB2_TSEG2_MIN, > + .tseg2_max = ESD_USB2_TSEG2_MAX, > + .sjw_max = ESD_USB2_SJW_MAX, > + .brp_min = ESD_USB2_BRP_MIN, > + .brp_max = ESD_USB2_BRP_MAX, > + .brp_inc = ESD_USB2_BRP_INC, > +}; > + > +static int esd_usb2_set_bittiming(struct net_device *netdev) > +{ > + struct esd_usb2_net_priv *priv = netdev_priv(netdev); > + struct can_bittiming *bt = &priv->can.bittiming; > + struct esd_usb2_msg msg; > + u32 canbtr; > + > + canbtr = ESD_USB2_UBR; > + canbtr |= (bt->brp - 1) & (ESD_USB2_BRP_MAX - 1); > + canbtr |= ((bt->sjw - 1) & (ESD_USB2_SJW_MAX - 1)) > + << ESD_USB2_SJW_SHIFT; > + canbtr |= ((bt->prop_seg + bt->phase_seg1 - 1) > + & (ESD_USB2_TSEG1_MAX - 1)) > + << ESD_USB2_TSEG1_SHIFT; > + canbtr |= ((bt->phase_seg2 - 1) & (ESD_USB2_TSEG2_MAX - 1)) > + << ESD_USB2_TSEG2_SHIFT; > + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) > + canbtr |= ESD_USB2_3_SAMPLES; > + > + msg.msg.hdr.len = 2; > + msg.msg.hdr.cmd = CMD_SETBAUD; > + msg.msg.setbaud.net = priv->index; > + msg.msg.setbaud.rsvd = 0; > + msg.msg.setbaud.baud = cpu_to_le32(canbtr); > + > + dev_info(ND2D(netdev), "setting BTR=%#x\n", canbtr); > + > + return esd_usb2_send_msg(priv->usb2, &msg); > +} > + > +static int esd_usb2_set_mode(struct net_device *netdev, enum can_mode mode) > +{ > + struct esd_usb2_net_priv *priv = netdev_priv(netdev); > + > + if (!priv->open_time) > + return -EINVAL; > + > + switch (mode) { > + case CAN_MODE_START: > + netif_wake_queue(netdev); > + break; > + > + default: > + return -EOPNOTSUPP; > + } > + > + return 0; > +} > + > +static int esd_usb2_probe_one_net(struct usb_interface *intf, int index) > +{ > + struct esd_usb2 *dev = usb_get_intfdata(intf); > + struct net_device *netdev; > + struct esd_usb2_net_priv *priv; > + int err; > + int i; > + > + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS); > + if (!netdev) { > + dev_err(&intf->dev, "couldn't alloc candev\n"); > + return -ENOMEM; > + } > + > + priv = netdev_priv(netdev); > + > + init_usb_anchor(&priv->tx_submitted); > + atomic_set(&priv->active_tx_jobs, 0); > + > + for (i = 0; i < MAX_TX_URBS; i++) > + priv->tx_contexts[i].echo_index = MAX_TX_URBS; > + > + priv->usb2 = dev; > + priv->netdev = netdev; > + priv->index = index; > + > + priv->can.state = CAN_STATE_STOPPED; > + priv->can.clock.freq = ESD_USB2_CAN_CLOCK; > + priv->can.bittiming_const = &esd_usb2_bittiming_const; > + priv->can.do_set_bittiming = esd_usb2_set_bittiming; > + priv->can.do_set_mode = esd_usb2_set_mode; > + > + netdev->flags |= IFF_ECHO; /* we support local echo */ > + > +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28) > + netdev->netdev_ops = &esd_usb2_netdev_ops; > +#else > + netdev->open = esd_usb2_open; > + netdev->stop = esd_usb2_close; > + netdev->hard_start_xmit = esd_usb2_start_xmit; > +#endif > + > + SET_NETDEV_DEV(netdev, &intf->dev); > + > + err = register_candev(netdev); > + if (err) { > + dev_err(&intf->dev, > + "couldn't register CAN device: %d\n", err); > + free_candev(netdev); > + return -ENOMEM; > + } > + > + dev->nets[index] = priv; > + dev_info(ND2D(netdev), "device %s registered\n", netdev->name); > + return 0; > +} > + > +/* > + * probe function for new USB2 devices > + * > + * check version information and number of available > + * CAN interfaces > + */ > +static int esd_usb2_probe(struct usb_interface *intf, > + const struct usb_device_id *id) > +{ > + struct esd_usb2 *dev; > + struct esd_usb2_msg msg; > + int i, err = -ENOMEM; > + > + dev = kzalloc(sizeof(*dev), GFP_KERNEL); > + if (!dev) > + return -ENOMEM; > + > + dev->udev = interface_to_usbdev(intf); > + > + init_usb_anchor(&dev->rx_submitted); > + > + usb_set_intfdata(intf, dev); > + > + /* query number of CAN interfaces (nets) */ > + msg.msg.hdr.cmd = CMD_VERSION; > + msg.msg.hdr.len = 2; > + msg.msg.version.rsvd = 0; > + msg.msg.version.flags = 0; > + msg.msg.version.drv_version = 0; > + > + if (esd_usb2_send_msg(dev, &msg) < 0) { > + dev_err(&intf->dev, "sending version message failed\n"); > + goto free_dev; > + } > + > + if (esd_usb2_wait_msg(dev, &msg) < 0) { > + dev_err(&intf->dev, "no version message answer\n"); > + goto free_dev; > + } > + > + dev->net_count = (int)msg.msg.version_reply.nets; > + dev->version = le32_to_cpu(msg.msg.version_reply.version); > + > +#ifdef CONFIG_SYSFS > + device_create_file(&intf->dev, &dev_attr_firmware); > + device_create_file(&intf->dev, &dev_attr_hardware); > + device_create_file(&intf->dev, &dev_attr_nets); > +#endif > + > + /* do per device probing */ > + for (i = 0; i < dev->net_count; i++) > + esd_usb2_probe_one_net(intf, i); > + > + return 0; > + > +free_dev: > + kfree(dev); > + return err; > +} > + > +/* > + * called by the usb core when the device is removed from the system > + */ > +static void esd_usb2_disconnect(struct usb_interface *intf) > +{ > + struct esd_usb2 *dev = usb_get_intfdata(intf); > + struct net_device *netdev; > + int i; > + > +#ifdef CONFIG_SYSFS > + device_remove_file(&intf->dev, &dev_attr_firmware); > + device_remove_file(&intf->dev, &dev_attr_hardware); > + device_remove_file(&intf->dev, &dev_attr_nets); > +#endif > + usb_set_intfdata(intf, NULL); > + > + if (dev) { > + for (i = 0; i < dev->net_count; i++) { > + if (dev->nets[i]) { > + netdev = dev->nets[i]->netdev; > + unregister_netdev(netdev); > + free_candev(netdev); > + } > + } > + unlink_all_urbs(dev); > + } > +} > + > +/* usb specific object needed to register this driver with the usb subsystem > */ > +static struct usb_driver esd_usb2_driver = { > + .name = "esd_usb2", > + .probe = esd_usb2_probe, > + .disconnect = esd_usb2_disconnect, > + .id_table = esd_usb2_table, > +}; > + > +static int __init esd_usb2_init(void) > +{ > + int err; > + > + printk(KERN_INFO "esd CAN/USB2 kernel driver\n"); > + > + /* register this driver with the USB subsystem */ > + err = usb_register(&esd_usb2_driver); > + > + if (err) { > + err("usb_register failed. Error number %d\n", err); > + return err; > + } > + > + return 0; > +} > + > +static void __exit esd_usb2_exit(void) > +{ > + /* deregister this driver with the USB subsystem */ > + usb_deregister(&esd_usb2_driver); > +} > + > +module_init(esd_usb2_init); > +module_exit(esd_usb2_exit); > Index: drivers/net/can/usb/Makefile > =================================================================== > --- drivers/net/can/usb/Makefile (revision 1095) > +++ drivers/net/can/usb/Makefile (working copy) > @@ -16,6 +16,7 @@ > -include $(TOPDIR)/Makefile.common > > obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o > +obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o > > ifeq ($(CONFIG_CAN_DEBUG_DEVICES),y) > EXTRA_CFLAGS += -DDEBUG > Index: drivers/net/can/Makefile > =================================================================== > --- drivers/net/can/Makefile (revision 1095) > +++ drivers/net/can/Makefile (working copy) > @@ -19,6 +19,7 @@ > export CONFIG_CAN_EMS_PCI=m > export CONFIG_CAN_EMS_PCMCIA=m > export CONFIG_CAN_ESD_PCI331=m > +export CONFIG_CAN_ESD_USB2=m > export CONFIG_CAN_PIPCAN=m > export CONFIG_CAN_SOFTING=m > export CONFIG_CAN_SOFTING_CS=m > _______________________________________________ > Socketcan-core mailing list > [email protected] > https://lists.berlios.de/mailman/listinfo/socketcan-core - -- Pengutronix e.K. | Marc Kleine-Budde | Industrial Linux Solutions | Phone: +49-231-2826-924 | Vertretung West/Dortmund | Fax: +49-5121-206917-5555 | Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de | -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iEYEARECAAYFAktLfmAACgkQjTAFq1RaXHO+gwCfTQVqowvkY+ky9AVSjWAOxQmN KfwAnjr+sMhxLAPtlkt0AkiR2Se9aRbP =x8Jl -----END PGP SIGNATURE----- _______________________________________________ Socketcan-core mailing list [email protected] https://lists.berlios.de/mailman/listinfo/socketcan-core
