All of lore.kernel.org
 help / color / mirror / Atom feed
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 --]

  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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.