Re: [PATCH, RFC] ems_usb: Added support for EMS CPC-USB/ARM7 CAN/USB interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Oliver Neukum schrieb:
> Am Samstag, 12. September 2009 12:28:40 schrieb Sebastian Haas:
>> The following patch adds a driver to support the CAN\USB interface
>> CPC-USB/ARM7. The patch was already posted on netdev for inclusion, but as
>> the driver is also a USB driver it would be nice if someone of the Linux
>> USB folks could take a look at the USB side of the driver. Thanks.
>>
> 
> I put comments among the code. I hope you find them useful.
> 
> 	Regards
> 		Oliver
> 
>> +
>> +/*
>> + * callback for bulk IN urb
>> + */
>> +static void ems_usb_write_bulk_callback(struct urb *urb)
>> +{
>> +	struct ems_tx_urb_context *context = urb->context;
>> +	struct ems_usb *dev;
>> +	struct net_device *netdev;
>> +
>> +	if (!context)
>> +		return;
> Can this happen?
No. Would a BUG_ON too paranoid?

>> +
>> +	dev = context->dev;
>> +	netdev = dev->netdev;
>> +
>> +	if (!netif_device_present(netdev))
>> +		return;
> 
> You don't free the buffer in the error case. Memory leak?
Oh, yes.

>> +
>> +	if (urb->status)
>> +		dev_info(netdev->dev.parent, "Tx URB aborted (%d)\n",
>> +			 urb->status);
>> +
>> +	/* free up our allocated buffer */
>> +	usb_buffer_free(urb->dev, urb->transfer_buffer_length,
>> +			urb->transfer_buffer, urb->transfer_dma);
>> +
>> +	netdev->trans_start = jiffies;
>> +
>> +	/* transmission complete interrupt */
>> +	netdev->stats.tx_packets++;
>> +	netdev->stats.tx_bytes += context->dlc;
>> +
>> +	can_get_echo_skb(netdev, context->echo_index);
>> +
>> +	/* Release context */
>> +	context->echo_index = MAX_TX_URBS;
>> +
>> +	atomic_dec(&dev->active_tx_urbs);
>> +
>> +	if (netif_queue_stopped(netdev))
>> +		netif_wake_queue(netdev);
>> +}
>> +
> [..]
>> +
>> +/*
>> + * Start interface
>> + */
>> +static int ems_usb_start(struct ems_usb *dev)
>> +{
>> +	struct net_device *netdev = dev->netdev;
>> +	int err, i;
>> +
>> +	dev->intr_in_buffer[0] = 0;
>> +	dev->free_slots = 15; /* initial size */
>> +
>> +	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_ATOMIC);
> 
> Why GFP_ATOMIC?
No reason, will change to GFP_KERNEL.

>> +		if (!urb) {
>> +			dev_err(netdev->dev.parent,
>> +				"No memory left for URBs\n");
>> +			return -ENOMEM;
>> +		}
>> +
>> +		buf = usb_buffer_alloc(dev->udev, RX_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;
>> +		}
>> +
>> +		usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2),
>> +				  buf, RX_BUFFER_SIZE,
>> +				  ems_usb_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) {
>> +			if (err == -ENODEV)
>> +				netif_device_detach(dev->netdev);
>> +
>> +			usb_unanchor_urb(urb);
>> +			usb_buffer_free(dev->udev, RX_BUFFER_SIZE, buf,
>> +					urb->transfer_dma);
>> +			break;
>> +		}
>> +
>> +		/* Drop reference, USB core will take care of freeing it */
>> +		usb_free_urb(urb);
>> +	}
>> +
>> +	/* Did we submit any URBs */
>> +	if (i == 0) {
>> +		dev_warn(netdev->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(netdev->dev.parent, "rx performance may be slow\n");
>> +
>> +	/* Setup and start interrupt URB */
>> +	usb_fill_int_urb(dev->intr_urb, dev->udev,
>> +			 usb_rcvintpipe(dev->udev, 1),
>> +			 dev->intr_in_buffer,
> 
> This is a DMA coherency bug. You need to allocate a separate buffer.
Okay, good hint. Didn't know anything about it. Will fix it.

>> +			 sizeof(dev->intr_in_buffer),
>> +			 ems_usb_read_interrupt_callback, dev, 1);
>> +
>> +	err = usb_submit_urb(dev->intr_urb, GFP_KERNEL);
>> +	if (err) {
>> +		if (err == -ENODEV)
>> +			netif_device_detach(dev->netdev);
>> +
>> +		dev_warn(netdev->dev.parent, "intr URB submit failed: %d\n",
>> +			 err);
>> +
>> +		return err;
>> +	}
>> +
>> +	/* CPC-USB will transfer received message to host */
>> +	err = ems_usb_control_cmd(dev, CONTR_CAN_MESSAGE | CONTR_CONT_ON);
>> +	if (err)
>> +		goto failed;
>> +
>> +	/* CPC-USB will transfer CAN state changes to host */
>> +	err = ems_usb_control_cmd(dev, CONTR_CAN_STATE | CONTR_CONT_ON);
>> +	if (err)
>> +		goto failed;
>> +
>> +	/* CPC-USB will transfer bus errors to host */
>> +	err = ems_usb_control_cmd(dev, CONTR_BUS_ERROR | CONTR_CONT_ON);
>> +	if (err)
>> +		goto failed;
>> +
>> +	err = ems_usb_write_mode(dev, SJA1000_MOD_NORMAL);
>> +	if (err)
>> +		goto failed;
>> +
>> +	dev->can.state = CAN_STATE_ERROR_ACTIVE;
>> +
>> +	return 0;
>> +
>> +failed:
>> +	if (err == -ENODEV)
>> +		netif_device_detach(dev->netdev);
>> +
>> +	dev_warn(netdev->dev.parent, "couldn't submit control: %d\n", err);
>> +
>> +	return err;
>> +}
> [..]
>> +
>> +static int ems_usb_start_xmit(struct sk_buff *skb, struct net_device
>> *netdev) +{
>> +	struct ems_usb *dev = netdev_priv(netdev);
>> +	struct ems_tx_urb_context *context = NULL;
>> +	struct net_device_stats *stats = &netdev->stats;
>> +	struct can_frame *cf = (struct can_frame *)skb->data;
>> +	struct ems_cpc_msg *msg;
>> +	struct urb *urb;
>> +	u8 *buf;
>> +	int i, err;
>> +	size_t size = CPC_HEADER_SIZE + CPC_MSG_HEADER_LEN
>> +			+ sizeof(struct cpc_can_msg);
>> +
>> +	/* create a URB, and a buffer for it, and copy the data to the URB */
>> +	urb = usb_alloc_urb(0, GFP_ATOMIC);
> 
> GFP_ATOMIC
> 
>> +	if (!urb) {
>> +		dev_err(netdev->dev.parent, "No memory left for URBs\n");
>> +		goto nomem;
>> +	}
>> +
>> +	buf = usb_buffer_alloc(dev->udev, size, GFP_KERNEL, &urb->transfer_dma);
> 
> GFP_KERNEL. Only one of these flags can be right.
GFP_ATOMIC should be right, as seen in other USB network drivers.

>> +	if (!buf) {
>> +		dev_err(netdev->dev.parent, "No memory left for USB buffer\n");
>> +		usb_free_urb(urb);
>> +		goto nomem;
>> +	}
>> +
>> +	msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE];
>> +
>> +	msg->msg.can_msg.id = cf->can_id & 0x1FFFFFFFU;
>> +	msg->msg.can_msg.length = cf->can_dlc;
>> +
>> +	if (cf->can_id & CAN_RTR_FLAG) {
>> +		msg->type = cf->can_id & CAN_EFF_FLAG ?
>> +			CPC_CMD_TYPE_EXT_RTR_FRAME : CPC_CMD_TYPE_RTR_FRAME;
>> +
>> +		msg->length = CPC_CAN_MSG_MIN_SIZE;
>> +	} else {
>> +		msg->type = cf->can_id & CAN_EFF_FLAG ?
>> +			CPC_CMD_TYPE_EXT_CAN_FRAME : CPC_CMD_TYPE_CAN_FRAME;
>> +
>> +		for (i = 0; i < cf->can_dlc; i++)
>> +			msg->msg.can_msg.msg[i] = cf->data[i];
>> +
>> +		msg->length = CPC_CAN_MSG_MIN_SIZE + cf->can_dlc;
>> +	}
>> +
>> +	for (i = 0; i < MAX_TX_URBS; i++) {
>> +		if (dev->tx_contexts[i].echo_index == MAX_TX_URBS) {
>> +			context = &dev->tx_contexts[i];
>> +			break;
>> +		}
>> +	}
>> +
>> +	/*
>> +	 * May never happen! When this happens we'd more URBs in flight as
>> +	 * allowed (MAX_TX_URBS).
>> +	 */
>> +	if (!context) {
>> +		usb_unanchor_urb(urb);
>> +		usb_buffer_free(dev->udev, size, buf, urb->transfer_dma);
>> +
>> +		dev_warn(netdev->dev.parent, "couldn't find free context\n");
>> +
>> +		return NETDEV_TX_BUSY;
>> +	}
>> +
>> +	context->dev = dev;
>> +	context->echo_index = i;
>> +	context->dlc = cf->can_dlc;
>> +
>> +	usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf,
>> +			  size, ems_usb_write_bulk_callback, context);
>> +	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
>> +	usb_anchor_urb(urb, &dev->tx_submitted);
>> +
>> +	can_put_echo_skb(skb, netdev, context->echo_index);
>> +
>> +	err = usb_submit_urb(urb, GFP_ATOMIC);
>> +	if (unlikely(err)) {
>> +		can_free_echo_skb(netdev, context->echo_index);
>> +
>> +		usb_unanchor_urb(urb);
>> +		usb_buffer_free(dev->udev, size, buf, urb->transfer_dma);
>> +		dev_kfree_skb(skb);
>> +
>> +		if (err == -ENODEV) {
>> +			netif_device_detach(netdev);
>> +		} else {
>> +			dev_warn(netdev->dev.parent, "failed tx_urb %d\n", err);
>> +
>> +			stats->tx_dropped++;
>> +		}
>> +	} else {
>> +		atomic_inc(&dev->active_tx_urbs);
> 
> The URB may have finished, thus dropping active_tx_urbs below zero.
Good tip. Will increment active_tx_urbs before submitting urb and decrement it
on error.

>> +
>> +		netdev->trans_start = jiffies;
>> +
>> +		/* Slow down tx path */
>> +		if (atomic_read(&dev->active_tx_urbs) >= MAX_TX_URBS ||
>> +		    dev->free_slots < 5) {
>> +			netif_stop_queue(netdev);
> 
> You don't call usb_free_urb()
> 
>> +			return NETDEV_TX_OK;
> 
> I'd simply drop the return.
Will fix it.

>> +		}
>> +	}
>> +
>> +	/*
>> +	 * Release our reference to this URB, the USB core will eventually free
>> +	 * it entirely.
>> +	 */
>> +	usb_free_urb(urb);
>> +
>> +	return NETDEV_TX_OK;
>> +
>> +nomem:
>> +	if (skb)
>> +		dev_kfree_skb(skb);
>> +
>> +	stats->tx_dropped++;
>> +
>> +	return NETDEV_TX_OK;
>> +}
>> +
>> +static int ems_usb_close(struct net_device *netdev)
>> +{
>> +	struct ems_usb *dev = netdev_priv(netdev);
>> +
>> +	netif_stop_queue(netdev);
>> +
>> +	/* Stop polling */
>> +	unlink_all_urbs(dev);
> 
> Looks like a race. A completing urb can call netif_wake_queue()
Swapped it.

>> +	/* Set CAN controller to reset mode */
>> +	if (ems_usb_write_mode(dev, SJA1000_MOD_RM))
>> +		dev_warn(netdev->dev.parent, "couldn't stop device");
>> +
>> +	close_candev(netdev);
>> +
>> +	dev->open_time = 0;
>> +
>> +	return 0;
>> +}

Thanks for your quick review, your comments where very helpful.

Cheers, Sebastian
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkqr8+IACgkQpqRB8PJG7XwRCwCgk98+LoIJoyMVBCzGbZgV9Alp
+jkAn1EvwKA7kFbePmkMP0e0XoQwqLR9
=zQCq
-----END PGP SIGNATURE-----
-- 
EMS Dr. Thomas Wuensche e.K.
Sonnenhang 3
85304 Ilmmuenster
HRA Neuburg a.d. Donau, HR-Nr. 70.106
Phone: +49-8441-490260
Fax  : +49-8441-81860
http://www.ems-wuensche.com
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux