From: Marc Kleine-Budde <mkl@pengutronix.de>
To: "Max S." <max@schneidersoft.net>
Cc: linux-can <linux-can@vger.kernel.org>,
Sven Geggus <lists@fuchsschwanzdomain.de>
Subject: Re: Usb to can driver
Date: Thu, 02 May 2013 13:07:59 +0200 [thread overview]
Message-ID: <5182490F.2080707@pengutronix.de> (raw)
In-Reply-To: <1366839193.7226.8.camel@blackbox>
[-- Attachment #1: Type: text/plain, Size: 27709 bytes --]
On 04/24/2013 11:33 PM, Max S. wrote:
> From 8a83080bcddc558cd016e58e408b56ca68bc5bc8 Mon Sep 17 00:00:00 2001
> From: Maximilian Schneider <max@schneidersoft.net>
> Date: Wed, 24 Apr 2013 21:03:31 +0000
> Subject: [PATCH] Added ss_usb source.
Comments inline.
> Signed-off-by: Maximilian Schneider <max@schneidersoft.net>
> ---
> drivers/net/can/usb/Makefile | 1 +
You should add your driver to Kconfig, too.
> drivers/net/can/usb/ss_usb.c | 944 ++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 945 insertions(+)
> create mode 100644 drivers/net/can/usb/ss_usb.c
>
> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> index becef46..c341a1b 100644
> --- a/drivers/net/can/usb/Makefile
> +++ b/drivers/net/can/usb/Makefile
> @@ -7,5 +7,6 @@ obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.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_SS_USB) += ss_usb.o
>
> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/usb/ss_usb.c b/drivers/net/can/usb/ss_usb.c
> new file mode 100644
> index 0000000..69710ff
> --- /dev/null
> +++ b/drivers/net/can/usb/ss_usb.c
> @@ -0,0 +1,944 @@
Please add a header stating your license.
> +#include <linux/init.h>
> +#include <linux/signal.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>
> +
> +/* Device specific constants */
> +#define USB_SSUSB_VENDOR_ID 0xbeef
> +#define USB_SSUSB_AT32_PRODUCT_ID 0x1234
Please get in touch with the Openmoko people for a real vendor/product
id as Sven suggested. Sven can you arrange a contect?
> +
> +#define SS_USB_ENDPOINT_IN 1
> +#define SS_USB_ENDPOINT_OUT 2
> +
> +#define SS_USB_BREQ_HOST_FORMAT 0
> +#define SS_USB_BREQ_BITTIMING 1
> +#define SS_USB_BREQ_MODE 2
> +#define SS_USB_BREQ_BERR 3
> +#define SS_USB_BREQ_BT_CONST 4
> +
> +
> +#define SS_CAN_MODE_RESET 0
> +#define SS_CAN_MODE_START 1
> +
> +#define SS_CAN_STATE_ERROR_ACTIVE 0
> +#define SS_CAN_STATE_ERROR_WARNING 1
> +#define SS_CAN_STATE_ERROR_PASSIVE 2
> +#define SS_CAN_STATE_BUS_OFF 3
> +#define SS_CAN_STATE_STOPPED 4
> +#define SS_CAN_STATE_SLEEPING 5
Can you pelase create an enum for each of these.
> +
> +/* data types passed between host and device */
> +struct __packed device_config {
> + u16 byte_order;
u32, byteorder please, so that you have natural alignment for this struct.
> +
> + u32 hf_size; /* size of a host frame */
> + u32 hf_can_id;
> + u32 hf_can_dlc;
> + u32 hf_data;
> + u32 hf_echo_id;
> + u32 hf_channel;
> +};
If your struct is always aligned naturally in mem, please add a
"__attribute__ ((packed, aligned(4)));"
The two above comments apply to all struct definitions here.
> +
> +struct __packed device_mode {
> + u8 mode;
u32
> +};
> +
> +struct __packed device_state {
> + u8 state;
u32
> + u32 rxerr;
> + u32 txerr;
> +};
> +
> +struct __packed device_bittiming {
> + u32 prop_seg;
> + u32 phase_seg1;
> + u32 phase_seg2;
> + u32 sjw;
> + u32 brp;
> +};
> +
> +struct __packed device_bt_const {
> + u32 fclk_can;
> + u32 tseg1_min;
> + u32 tseg1_max;
> + u32 tseg2_min;
> + u32 tseg2_max;
> + u32 sjw_max;
> + u32 brp_min;
> + u32 brp_max;
> + u32 brp_inc;
> +};
> +
> +struct ss_host_frame {
> + u8 channel;
u32
> + u32 echo_id;
> + struct can_frame frame;
> +};
> +
> +#define MAX_TX_URBS 10
> +#define MAX_RX_URBS 10
> +
> +#define MAX_INTF 2
> +
> +struct ss_tx_context {
> + struct ss_can *dev;
> + unsigned int echo_id;
> +};
> +
> +struct ss_can {
> + struct can_priv can; /* must be the first member */
> +
> + struct ss_usb *parent;
> +
> + struct net_device *netdev;
> + struct usb_device *udev;
> + struct usb_interface *iface;
> +
> + struct can_bittiming_const bt_const;
> + unsigned int channel; /* channel number */
> +
> + struct ss_tx_context tx_context[MAX_TX_URBS];
> +
> + struct usb_anchor tx_submitted;
> + atomic_t active_tx_urbs;
> +
> + int open_time;
Please remove it and all references to it, not needed anymore
> +};
> +
> +/* usb interface struct */
> +struct ss_usb {
> + struct ss_can *canch[MAX_INTF];
> +
> + struct usb_anchor rx_submitted;
> +
> + atomic_t active_channels;
> +};
> +
> +/* 'allocate' a tx context.
> + * returns a valid tx context or NULL if there is no space.
> + */
> +static struct ss_tx_context *ss_alloc_tx_context(struct ss_can *dev)
> +{
> + int ii = 0;
We usually only use 'i' for simple for loops.
> + for (; ii < MAX_TX_URBS; ii++)
> + if (dev->tx_context[ii].echo_id == MAX_TX_URBS) {
> + dev->tx_context[ii].echo_id = ii;
> + return &dev->tx_context[ii];
> + }
> + return NULL;
> +}
> +
> +/* releases a tx context
> + */
> +static void ss_free_tx_context(struct ss_tx_context *txc)
> +{
> + txc->echo_id = MAX_TX_URBS;
> +}
> +
> +/* Get a tx context by id.
> + */
> +static struct ss_tx_context *ss_get_tx_context(
> + struct ss_can *dev,
> + unsigned int id)
Does this fit into one line? I don't enforce the 90 columns rule for
functions prototypes.
> +{
> + if (id < MAX_TX_URBS)
> + if (dev->tx_context[id].echo_id == id)
> + return &dev->tx_context[id];
> +
> + return NULL;
> +}
> +
> +/* Get a tx contexts id.
> + */
> +static unsigned int ss_tx_context_id(struct ss_tx_context *txc)
> +{
> + return txc->echo_id;
> +}
> +
> +#define BUFFER_SIZE sizeof(struct ss_host_frame)
> +
> +/**************************** USB CALLBACKS **************************/
> +static void ss_usb_recieve_bulk_callback(struct urb *urb)
> +{
> + struct ss_usb *usbcan = urb->context;
> + struct ss_can *dev;
> + struct net_device *netdev;
> + int rc;
> + struct net_device_stats *stats;
> + struct sk_buff *skb;
> + struct can_frame *cf;
> + struct ss_host_frame *hf = urb->transfer_buffer;
> + struct ss_tx_context *txc;
> +
> + BUG_ON(!usbcan);
> +
> + switch (urb->status) {
> + case 0: /* success */
> + break;
> + case -ENOENT:
> + case -ESHUTDOWN:
> + return;
> + default:
> + /* printk(KERN_INFO "Rx URB aborted (%d)\n", urb->status); */
Use netdev_info() and friends or remove the code. Have a look at the
other usb drivers e.g. the drivers/net/can/usb/usb_8dev.c, which is
quite new.
> + /* do not resubmit aborted urbs. eg: when device goes down */
> + return;
> + }
> +
> + /* device reports out of range channel id */
> + if (hf->channel >= MAX_INTF)
> + /* printk(KERN_INFO "%d] strange channel #\n", hf->channel); */
same here
> + goto resubmit_urb;
> +
> + /* device reports bad channel id */
> + dev = usbcan->canch[hf->channel];
> +
> + BUG_ON(!dev);
> +
> + netdev = dev->netdev;
> + BUG_ON(!netdev);
I think you can remove most of the BUG_ONs in the code.
> +
> + if (!netif_device_present(netdev))
> + return;
> +
> + stats = &dev->netdev->stats;
> +
> + if (hf->echo_id == 0) { /* normal */
> + netdev_info(netdev, "%d] got msg\n", hf->channel);
> + skb = alloc_can_skb(dev->netdev, &cf);
> + if (skb == NULL)
> + return;
> +
> + *cf = hf->frame;
> + cf->can_dlc = get_can_dlc(hf->frame.can_dlc);
> +
> + netif_rx(skb);
> +
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> + } else { /* echo_id = hf->echo_id-1 */
> + txc = ss_get_tx_context(dev, hf->echo_id-1);
> +
> + /* bad devices send bad echo_ids. */
> + if (!txc) {
> + netdev_info(netdev, "%d] bad echo %d\n",
> + hf->channel, hf->echo_id);
> + goto resubmit_urb;
> + } else {
> + netdev_info(netdev, "%d] got echo %d\n",
> + hf->channel, hf->echo_id);
> + }
> +
> + can_get_echo_skb(netdev, ss_tx_context_id(txc));
> +
> + ss_free_tx_context(txc);
> +
> + netdev->stats.tx_packets++;
> + /* FIXME: report dlc: stats->tx_bytes += cf->can_dlc; */
> +
> + netdev->trans_start = jiffies;
should not be needed
> +
> + if (netif_queue_stopped(netdev))
> + netif_wake_queue(netdev);
just call netif_wake_queue(netdev); unconditionally
> + }
> +
> +
> +resubmit_urb:
> + /* do i really need to refill? the urb should still be valid:
> + usb_fill_bulk_urb(urb,
> + dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + SS_USB_ENDPOINT_IN(dev->iface->altsetting[0].desc.bInterfaceNumber)),
> + urb->transfer_buffer,
> + BUFFER_SIZE, ss_usb_recieve_bulk_callback,
> + dev);
> + */
> + rc = usb_submit_urb(urb, GFP_ATOMIC);
> +
> +/* How to best handle errore?
Have a look at existing CAN USB drivers
> + if (rc == -ENODEV) {
> + printk(KERN_INFO "resubmit error -ENODEV\n");
> + FIXME: necessary?
> + netif_device_detach(netdev);
> +
> + } else if (rc) {
> + printk(KERN_INFO "resubmit error %d\n", rc);
> + }
> +*/
> +}
> +
> +static void ss_usb_xmit_callback(struct urb *urb)
> +{
> + struct ss_tx_context *txc = urb->context;
> + struct ss_can *dev;
> + struct net_device *netdev;
> +
> + BUG_ON(!txc);
> +
> + dev = txc->dev;
> + BUG_ON(!dev);
> +
> + netdev = dev->netdev;
> + BUG_ON(!netdev);
> +
> + if (urb->status) {
> + dev_info(netdev->dev.parent, "%d] xmit err %d\n",
> + dev->channel, txc->echo_id+1);
> + } else {
> + dev_info(netdev->dev.parent, "%d] xmit ok %d\n",
> + dev->channel, txc->echo_id+1);
> + }
> +
> + usb_free_coherent(urb->dev,
> + urb->transfer_buffer_length,
> + urb->transfer_buffer,
> + urb->transfer_dma);
> +
> + atomic_dec(&dev->active_tx_urbs);
> +
> + if (!netif_device_present(netdev))
> + return;
> +
> + /* The urb never made it to the device. Drop this packet. */
> + if (urb->status) {
> + dev_info(netdev->dev.parent, "Tx URB aborted (%d)\n",
> + urb->status);
> + can_free_echo_skb(netdev, ss_tx_context_id(txc));
> + ss_free_tx_context(txc);
> + netdev->stats.tx_dropped++;
> + }
> +
> + if (netif_queue_stopped(netdev))
> + netif_wake_queue(netdev);
> +}
> +
> +/****************************** UBS CAN ******************************/
> +static int ss_usb_set_bittiming(struct net_device *netdev)
> +{
> + struct ss_can *dev = netdev_priv(netdev);
> + struct can_bittiming *bt = &dev->can.bittiming;
> + struct usb_interface *intf = dev->iface;
> + int rc;
> +
> + struct __packed device_bittiming dbt = {
> + .prop_seg = bt->prop_seg,
> + .phase_seg1 = bt->phase_seg1,
> + .phase_seg2 = bt->phase_seg2,
> + .sjw = bt->sjw,
> + .brp = bt->brp
> + };
> +
> +/* printk(KERN_INFO "CH: %d\n", dev->channel);
> + printk(KERN_INFO "PRS %d\n", bt->prop_seg);
> + printk(KERN_INFO "PH1 %d\n", bt->phase_seg1);
> + printk(KERN_INFO "PH2 %d\n", bt->phase_seg2);
> + printk(KERN_INFO "SJW %d\n", bt->sjw);
> + printk(KERN_INFO "BRP %d\n", bt->brp);
> +*/
please remove
> +
> + rc = usb_control_msg(interface_to_usbdev(intf),
> + usb_sndctrlpipe(interface_to_usbdev(intf), 0),
> + SS_USB_BREQ_BITTIMING,
> + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
> + dev->channel,
> + 0,
> + &dbt,
> + sizeof(dbt),
> + 1000);
> + if (rc < 0)
> + dev_warn(netdev->dev.parent, "Couldn't set bittimings %d", rc);
> +
> + return rc;
> +}
> +
> +static int ss_usb_set_mode(struct net_device *netdev, enum can_mode mode)
> +{
> + struct ss_can *dev = netdev_priv(netdev);
> + struct usb_interface *intf = dev->iface;
> + struct device_mode dm;
> + int rc;
> + dev_warn(netdev->dev.parent, "ss can mode %d\n", mode);
> +
> + if (!dev->open_time)
> + return -EINVAL;
> +
> +
> + switch (mode) {
> + case CAN_MODE_START:
> + dm.mode = SS_CAN_MODE_RESET;
> +
> + rc = usb_control_msg(interface_to_usbdev(intf),
> + usb_sndctrlpipe(interface_to_usbdev(intf), 0),
> + SS_USB_BREQ_MODE,
> + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
> + dev->channel,
> + 0,
> + &dm,
> + sizeof(dm),
> + 1000);
> + if (rc < 0) {
> + dev_warn(netdev->dev.parent, "Couldn't start device %d",
> + rc);
> + return rc;
> + }
> +
> + if (netif_queue_stopped(netdev))
> + netif_wake_queue(netdev);
call unconditionally
> + break;
> + case CAN_MODE_STOP:
> + case CAN_MODE_SLEEP:
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int ss_usb_get_state(const struct net_device *netdev,
> + enum can_state *state)
> +{
> +
> + struct ss_can *dev = netdev_priv(netdev);
> + struct usb_interface *intf = dev->iface;
> + struct device_state dstate;
> + int rc;
> + dev_warn(netdev->dev.parent, "ss can state");
> +
> + rc = usb_control_msg(interface_to_usbdev(intf),
> + usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
> + SS_USB_BREQ_BERR,
> + USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
> + dev->channel,
> + 0,
> + &dstate,
> + sizeof(dstate),
> + 1000);
> + if (rc < 0) {
> + dev_warn(netdev->dev.parent, "Couldn't get state %d", rc);
> + return rc;
> + }
> +
> + netdev_info(netdev, "state: %d\n", dstate.state);
> +
> + switch (dstate.state) {
> + /* RX/TX error count < 96 */
> + case SS_CAN_STATE_ERROR_ACTIVE:
> + *state = CAN_STATE_ERROR_ACTIVE;
> + return 0;
> + /* RX/TX error count < 128 */
> + case SS_CAN_STATE_ERROR_WARNING:
> + *state = CAN_STATE_ERROR_WARNING;
> + return 0;
> + /* RX/TX error count < 256 */
> + case SS_CAN_STATE_ERROR_PASSIVE:
> + *state = CAN_STATE_ERROR_PASSIVE;
> + return 0;
> + /* RX/TX error count >= 256 */
> + case SS_CAN_STATE_BUS_OFF:
> + *state = CAN_STATE_BUS_OFF;
> + return 0;
> + /* Device is stopped */
> + case SS_CAN_STATE_STOPPED:
> + *state = CAN_STATE_STOPPED;
> + return 0;
> + /* Device is sleeping */
> + case SS_CAN_STATE_SLEEPING:
> + *state = CAN_STATE_SLEEPING;
> + return 0;
> + }
> +
> + return -ENOTSUPP;
> +}
> +
> +static int ss_usb_get_berr_counter(const struct net_device *netdev,
> + struct can_berr_counter *bec)
> +{
> + struct ss_can *dev = netdev_priv(netdev);
> + struct usb_interface *intf = dev->iface;
> + struct device_state dstate;
> + int rc;
> +
> + rc = usb_control_msg(interface_to_usbdev(intf),
> + usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
> + SS_USB_BREQ_BERR,
> + USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
> + dev->channel,
> + 0,
> + &dstate,
> + sizeof(dstate),
> + 1000);
> + if (rc < 0) {
> + dev_warn(netdev->dev.parent, "Couldn't get error counters %d",
> + rc);
> + return rc;
> + }
> +
> + dev_info(netdev->dev.parent, "txerr: %d rxerr: %d\n",
> + dstate.txerr, dstate.rxerr);
> +
> + bec->txerr = dstate.txerr;
> + bec->rxerr = dstate.rxerr;
> +
> + return 0;
> +}
> +
> +static netdev_tx_t ss_can_start_xmit(struct sk_buff *skb,
> + struct net_device *netdev)
> +{
> + struct ss_can *dev = netdev_priv(netdev);
> + struct net_device_stats *stats = &dev->netdev->stats;
> + struct urb *urb;
> + struct ss_host_frame *hf;
> + int rc;
> + struct ss_tx_context *txc;
> +
> + if (can_dropped_invalid_skb(netdev, skb))
> + return NETDEV_TX_OK;
> +
> + /* find an empty context to keep track of transmission */
> + txc = ss_alloc_tx_context(dev);
> + if (!txc) {
> + /* dev_kfree_skb(skb); */
> + return NETDEV_TX_BUSY;
> + }
> +
> + /* create a URB, and a buffer for it */
> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> + if (!urb) {
> + ss_free_tx_context(txc);
> + dev_err(netdev->dev.parent, "No memory left for URBs\n");
> + goto nomem;
> + }
> +
> + hf = usb_alloc_coherent(dev->udev, sizeof(*hf), GFP_ATOMIC,
> + &urb->transfer_dma);
> + if (!hf) {
> + ss_free_tx_context(txc);
> + dev_err(netdev->dev.parent, "No memory left for USB buffer\n");
> + usb_free_urb(urb);
> + goto nomem;
> + }
> +
> + hf->frame = *(struct can_frame *)skb->data;
> + hf->echo_id = ss_tx_context_id(txc)+1;
> + hf->channel = dev->channel;
> +
> + usb_fill_bulk_urb(urb, dev->udev,
> + usb_sndbulkpipe(dev->udev, SS_USB_ENDPOINT_OUT),
> + hf,
> + sizeof(*hf),
> + ss_usb_xmit_callback,
> + txc);
> +
> + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> + usb_anchor_urb(urb, &dev->tx_submitted);
> +
> + can_put_echo_skb(skb, netdev, ss_tx_context_id(txc));
> +
> + atomic_inc(&dev->active_tx_urbs);
> +
> +/* printk(KERN_INFO "%d] xmit new %d\n",hf->channel,hf->echo_id); */
> +
> + rc = usb_submit_urb(urb, GFP_ATOMIC);
> + if (unlikely(rc)) { /* usb send failed */
> + dev_err(netdev->dev.parent, "usb_submit failed %d\n", rc);
> +
> + can_free_echo_skb(netdev, ss_tx_context_id(txc));
> + ss_free_tx_context(txc);
> +
> + usb_unanchor_urb(urb);
> + usb_free_coherent(dev->udev,
> + sizeof(*hf),
> + hf,
> + urb->transfer_dma);
> +
> + dev_kfree_skb(skb);
> +
> + atomic_dec(&dev->active_tx_urbs);
> +
> + if (rc == -ENODEV) {
> + netif_device_detach(netdev);
> + } else {
> + dev_warn(netdev->dev.parent, "failed tx_urb %d\n", rc);
> +
> + stats->tx_dropped++;
> + }
> + } else {
> + netdev->trans_start = jiffies;
> +
> + /* Slow down tx path */
> + if (atomic_read(&dev->active_tx_urbs) >= MAX_TX_URBS)
> + netif_stop_queue(netdev);
> + }
> +
> + /* let usb core take care of this urb */
> + usb_free_urb(urb);
> +
> + return NETDEV_TX_OK;
> +
> +nomem:
> + dev_kfree_skb(skb);
> + stats->tx_dropped++;
> + return NETDEV_TX_OK;
> +}
> +
> +static int ss_can_open(struct net_device *netdev)
> +{
> + struct ss_can *dev = netdev_priv(netdev);
> + struct ss_usb *parent = dev->parent;
> + struct device_mode dm;
> + int rc, i;
> +
> + /* common open */
> + rc = open_candev(netdev);
> + if (rc)
> + return rc;
> +
> + if (atomic_add_return(1, &parent->active_channels) == 1) {
> + for (i = 0; i < MAX_RX_URBS; i++) {
> + struct urb *urb = NULL;
> + u8 *buf = NULL;
> +
> + /* alloc rx urb */
> + urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!urb) {
> + dev_err(netdev->dev.parent,
> + "No memory left for URBs\n");
> + return -ENOMEM;
> + }
> +
> + /* alloc rx buffer */
> + /* FIXME: free this memory */
> + buf = usb_alloc_coherent(dev->udev,
> + BUFFER_SIZE,
> + GFP_KERNEL,
> + &urb->transfer_dma);
> + if (!buf) {
> + dev_err(netdev->dev.parent,
> + "No memory left for USB buffer\n");
> + usb_free_urb(urb);
> + return -ENOMEM;
> + }
> +
> +
> + /* fill, anchor, and submit rx urb */
> + usb_fill_bulk_urb(urb,
> + dev->udev,
> + usb_rcvbulkpipe(dev->udev,
> + SS_USB_ENDPOINT_IN),
> + buf,
> + BUFFER_SIZE,
> + ss_usb_recieve_bulk_callback,
> + parent
> + );
> + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> + usb_anchor_urb(urb, &parent->rx_submitted);
> +
> + rc = usb_submit_urb(urb, GFP_KERNEL);
> + if (rc) {
> + if (rc == -ENODEV)
> + netif_device_detach(dev->netdev);
> +
> + /* FIXME: free rx buffer/urb maybe? */
> + usb_unanchor_urb(urb);
> + break;
> + }
> +
> + /* Drop reference,
> + * USB core will take care of freeing it
> + */
> + usb_free_urb(urb);
> + }
> + }
> +
> + /* finally start device */
> + dm.mode = SS_CAN_MODE_START;
> + rc = usb_control_msg(interface_to_usbdev(dev->iface),
> + usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0),
> + SS_USB_BREQ_MODE,
> + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
> + dev->channel,
> + 0,
> + &dm,
> + sizeof(dm),
> + 1000);
> +
> + if (rc < 0)
> + dev_warn(netdev->dev.parent, "Couldn't start device %d", rc);
> +
> + dev->open_time = jiffies;
> +
> + netif_start_queue(netdev);
> +
> + netdev_info(netdev, "ss can close\n");
> + return 0;
> +}
> +
> +/* think: ip link set canx down */
> +static int ss_can_close(struct net_device *netdev)
> +{
> + struct ss_can *dev = netdev_priv(netdev);
> + struct ss_usb *parent = dev->parent;
> +
> + netif_stop_queue(netdev);
> +
> + /* Stop polling */
> + if (atomic_dec_and_test(&parent->active_channels)) {
> + usb_kill_anchored_urbs(&parent->rx_submitted);
> + /* FIXME: Set CAN controller to reset mode */
> + }
> +
> + close_candev(netdev);
> +
> + dev->open_time = 0;
> +
> + netdev_info(netdev, "ss can close\n");
> + return 0;
> +}
> +
> +/******************************** USB ********************************/
> +
> +static const struct net_device_ops ss_usb_netdev_ops = {
> + .ndo_open = ss_can_open,
> + .ndo_stop = ss_can_close,
> + .ndo_start_xmit = ss_can_start_xmit,
> +};
> +
> +static int ss_make_candev(struct ss_can **dev, unsigned int channel,
> + struct usb_interface *intf);
> +static void ss_destroy_candev(struct ss_can *dev);
Please rearrange your code, that you don't need forward decarations.
> +
> +static int ss_usb_probe(struct usb_interface *intf,
> + const struct usb_device_id *id)
> +{
> + struct ss_usb *dev;
> + int rc = -ENOMEM;
> + unsigned int icount, i;
> +
> + struct device_config conf;
> +
> + /* send host config */
> + conf = (struct device_config){
> + .byte_order = 0xbeef,
> + .hf_size = sizeof(struct ss_host_frame),
> + .hf_can_id = offsetof(struct ss_host_frame, frame.can_id),
> + .hf_can_dlc = offsetof(struct ss_host_frame, frame.can_dlc),
> + .hf_data = offsetof(struct ss_host_frame, frame.data),
> + .hf_echo_id = offsetof(struct ss_host_frame, echo_id),
> + .hf_channel = offsetof(struct ss_host_frame, channel),
> + };
please use C99 initializers.
struct device_config conf = {
.hf_size = sizeof(struct ss_host_frame),
...
};
> +
> + rc = usb_control_msg(interface_to_usbdev(intf),
> + usb_sndctrlpipe(interface_to_usbdev(intf), 0),
> + SS_USB_BREQ_HOST_FORMAT,
> + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
> + 1,
> + intf->altsetting[0].desc.bInterfaceNumber,
> + &conf,
> + sizeof(conf),
> + 1000);
> +
> + if (rc < 0) {
> + dev_err(&intf->dev, "ss_usb: Couldn't send data format %d\n",
> + rc);
> + return rc;
> + }
> +
> + /* FIXME: fetch interface count and global settings etc.. */
> + icount = 2;
> +
> + if (icount > MAX_INTF) {
> + dev_err(&intf->dev,
> + "ss_usb: Cannot handle more that %d Can interfaces\n",
> + MAX_INTF);
> + return -EINVAL;
> + }
> +
> + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> + init_usb_anchor(&dev->rx_submitted);
> +
> + atomic_set(&dev->active_channels, 0);
> +
> + usb_set_intfdata(intf, dev);
> +
> + for (i = 0; i < icount; i++) {
> + rc = ss_make_candev(&dev->canch[i], i, intf);
> + if (rc) {
> + icount = i;
> + for (i = 0; i < icount; i++) {
> + ss_destroy_candev(dev->canch[i]);
> + dev->canch[icount] = NULL;
> + }
> + return rc;
> + }
> + dev->canch[i]->parent = dev;
> + }
> +
> + dev_info(&intf->dev, "ss usb probe\n");
> + return 0;
> +}
> +
> +static void ss_usb_disconnect(struct usb_interface *intf)
> +{
> + unsigned i;
> + struct ss_usb *dev = usb_get_intfdata(intf);
> + usb_set_intfdata(intf, NULL);
> +
> + if (!dev) {
> + dev_info(&intf->dev, "ss usb disconnect (nodata)\n");
> + return;
> + }
> +
> + for (i = 0; i < MAX_INTF; i++) {
> + struct ss_can *can = dev->canch[i];
> +
> + if (!can)
> + continue;
> +
> + unregister_netdev(can->netdev);
> + free_candev(can->netdev);
> +
> + usb_kill_anchored_urbs(&can->tx_submitted);
> + }
> +
> + usb_kill_anchored_urbs(&dev->rx_submitted);
> +
> + dev_info(&intf->dev, "ss usb disconnect\n");
> +}
> +
> +MODULE_DEVICE_TABLE(usb, ss_usb_table);
> +static struct usb_device_id ss_usb_table[] = {
> + {USB_DEVICE(USB_SSUSB_VENDOR_ID, USB_SSUSB_AT32_PRODUCT_ID)},
> + {} /* Terminating entry */
> +};
> +
> +static struct usb_driver ss_usb_driver = {
> + .name = "ss_usb",
> + .probe = ss_usb_probe,
> + .disconnect = ss_usb_disconnect,
> + .id_table = ss_usb_table,
> + /* FIXME: suspend, resume, etc... */
> +};
> +
> +static int __init ss_usb_init(void)
> +{
> + return usb_register(&ss_usb_driver);
> +}
> +
> +static void __exit ss_usb_exit(void)
> +{
> + usb_deregister(&ss_usb_driver);
> +}
> +
> +module_init(ss_usb_init);
> +module_exit(ss_usb_exit);
> +
> +MODULE_AUTHOR("Maximilian Schneider <mws@schneidersoft.net>");
> +MODULE_DESCRIPTION(
> +"socket CAN device driver for SchneiderSoft USB2.0 to CAN interfaces.");
> +MODULE_LICENSE("GPL v2");
> +
> +
> +
> +
> +
> +static int ss_make_candev(struct ss_can **out,
> + unsigned int channel,
> + struct usb_interface *intf)
> +{
> + struct ss_can *dev;
> + struct device_bt_const bt_const;
> + struct net_device *netdev;
> + int rc;
> + /* fetch bit timing constants */
> + rc = usb_control_msg(interface_to_usbdev(intf),
> + usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
> + SS_USB_BREQ_BT_CONST,
> + USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
> + channel,
> + 0,
> + &bt_const,
> + sizeof(bt_const),
> + 1000);
> +
> + if (rc < 0) {
> + dev_err(&intf->dev,
> + "ss_usb: Couldn't get bit timing const for channel %d\n",
> + rc);
> + return rc;
> + }
> +
> + dev_info(&intf->dev, "fclock: %d\n", bt_const.fclk_can);
> +
> + /* create netdev */
> + netdev = alloc_candev(sizeof(struct ss_can), MAX_TX_URBS);
> + if (!netdev) {
> + dev_err(&intf->dev, "ss_usb: Couldn't alloc candev\n");
> + return -ENOMEM;
> + }
> +
> + dev = netdev_priv(netdev);
> +
> + netdev->netdev_ops = &ss_usb_netdev_ops;
> +
> + netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */
> +
> + /* dev settup */
> + dev->bt_const = (struct can_bittiming_const){
> + .name = "ss_usb",
> + .tseg1_min = bt_const.tseg1_min,
> + .tseg1_max = bt_const.tseg1_max,
> + .tseg2_min = bt_const.tseg2_min,
> + .tseg2_max = bt_const.tseg2_max,
> + .sjw_max = bt_const.sjw_max,
> + .brp_min = bt_const.brp_min,
> + .brp_max = bt_const.brp_max,
> + .brp_inc = bt_const.brp_inc
> + };
> +
> + dev->udev = interface_to_usbdev(intf);
> + dev->iface = intf;
> + dev->netdev = netdev;
> + dev->channel = channel;
> +
> + init_usb_anchor(&dev->tx_submitted);
> + atomic_set(&dev->active_tx_urbs, 0);
> + for (rc = 0; rc < MAX_TX_URBS; rc++) {
> + dev->tx_context[rc].dev = dev;
> + dev->tx_context[rc].echo_id = MAX_TX_URBS;
> + }
> +
> + /* can settup */
> + dev->can.state = CAN_STATE_STOPPED;
> + dev->can.clock.freq = bt_const.fclk_can;
> + dev->can.bittiming_const = &dev->bt_const;
> + dev->can.do_set_bittiming = ss_usb_set_bittiming;
> + dev->can.do_set_mode = ss_usb_set_mode;
> + dev->can.do_get_state = ss_usb_get_state;
> + dev->can.do_get_berr_counter = ss_usb_get_berr_counter;
> + dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> +
> + SET_NETDEV_DEV(netdev, &intf->dev);
> +
> + rc = register_candev(dev->netdev);
> + if (rc) {
> + free_candev(dev->netdev);
> + dev_err(&intf->dev, "ss_usb: Couldn't register candev\n");
> + return rc;
> + }
> +
> + *out = dev;
> + return 0;
> +}
> +
> +static void ss_destroy_candev(struct ss_can *dev)
> +{
> + unregister_candev(dev->netdev);
> + free_candev(dev->netdev);
> +}
> +
> -- 1.7.10.4
>
Marc
--
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 |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 263 bytes --]
next prev parent reply other threads:[~2013-05-02 11:08 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-04-23 17:15 Usb to can driver Max S.
2013-04-23 21:47 ` Marc Kleine-Budde
2013-04-24 15:48 ` Max S.
2013-04-24 16:07 ` Marc Kleine-Budde
2013-04-24 17:40 ` Oliver Hartkopp
2013-04-24 21:24 ` Marc Kleine-Budde
2013-04-25 23:35 ` Max S.
2013-04-26 5:25 ` Oliver Hartkopp
2013-04-26 8:55 ` Kurt Van Dijck
2013-04-26 8:26 ` Marc Kleine-Budde
2013-04-24 21:33 ` Max S.
2013-05-02 11:07 ` Marc Kleine-Budde [this message]
2013-05-02 11:09 ` Marc Kleine-Budde
2013-05-02 11:30 ` Wolfgang Grandegger
2013-05-02 11:32 ` Marc Kleine-Budde
2013-05-16 11:40 ` Marc Kleine-Budde
2013-06-04 13:18 ` Max S.
2013-06-04 14:40 ` Wolfgang Grandegger
2013-06-04 14:41 ` Marc Kleine-Budde
2013-04-24 6:38 ` Sven Geggus
-- strict thread matches above, loose matches on Subject: below --
2013-06-25 23:59 Max S.
2013-06-26 7:10 ` wg
2013-06-26 18:55 ` Max S.
2013-06-26 18:58 ` Marc Kleine-Budde
[not found] ` <1372810462.15632.2.camel@blackbox>
2013-07-03 7:55 ` Marc Kleine-Budde
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5182490F.2080707@pengutronix.de \
--to=mkl@pengutronix.de \
--cc=linux-can@vger.kernel.org \
--cc=lists@fuchsschwanzdomain.de \
--cc=max@schneidersoft.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).