[PATCH v2 12/16] can: ems_usb: Fixed ems_usb_start_xmit for CAN FD

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

 



Added code to let ems_usb_start_xmit handle CAN FD messages

Signed-off-by: Gerhard Uttenthaler <uttenthaler@xxxxxxxxxxxxxxxx>
---
 drivers/net/can/usb/ems_usb.c | 143 ++++++++++++++++++++++++++--------
 1 file changed, 109 insertions(+), 34 deletions(-)

diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c
index 6d8f733c6c7f..76d25ab5804b 100644
--- a/drivers/net/can/usb/ems_usb.c
+++ b/drivers/net/can/usb/ems_usb.c
@@ -50,6 +50,7 @@ MODULE_LICENSE("GPL v2");
 #define CPC_CMD_TYPE_CAN_STATE     14  /* CAN state message */
 #define CPC_CMD_TYPE_EXT_CAN_FRAME 15  /* Extended CAN data frame */
 #define CPC_CMD_TYPE_EXT_RTR_FRAME 16  /* Extended CAN remote frame */
+#define CPC_CMD_TYPE_CANFD_FRAME   26  /* CAN FD frame */
 #define CPC_CMD_TYPE_CAN_EXIT      200 /* exit the CAN */
 
 #define CPC_CMD_TYPE_INQ_ERR_COUNTER 25 /* request the CAN error counters */
@@ -74,6 +75,7 @@ MODULE_LICENSE("GPL v2");
 /* Size of the "struct ems_cpc_msg" without the union */
 #define CPC_MSG_HEADER_LEN   11
 #define CPC_CAN_MSG_MIN_SIZE 5
+#define CPC_CANFD_MSG_MIN_SIZE 6
 
 /* Define these values to match your devices */
 #define USB_CPCUSB_VENDOR_ID 0x12D6
@@ -902,50 +904,123 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne
 	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);
+	u8 dlc;
+
+	u8 *buf;
+	size_t buf_size;
+	size_t buf_len = CPC_HEADER_SIZE + CPC_MSG_HEADER_LEN;
 
 	if (can_dropped_invalid_skb(netdev, skb))
 		return NETDEV_TX_OK;
 
-	/* create a URB, and a buffer for it, and copy the data to the URB */
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
-	if (!urb)
-		goto nomem;
-
-	buf = usb_alloc_coherent(dev->udev,
-				 size,
-				 GFP_ATOMIC,
-				 &urb->transfer_dma);
-	if (!buf) {
-		netdev_err(netdev, "No memory left for USB buffer\n");
-		usb_free_urb(urb);
-		goto nomem;
-	}
+	if (can_is_canfd_skb(skb)) {
+		struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
+		struct cpc_canfd_msg *fd_msg;
+
+		buf_size = CPC_HEADER_SIZE +
+			   CPC_MSG_HEADER_LEN +
+			   sizeof(struct cpc_canfd_msg);
+
+		/* Create an URB and a buffer big enough for
+		 * all message lengths, copy the data to the URB
+		 */
+		urb = usb_alloc_urb(0, GFP_ATOMIC);
+		if (!urb)
+			goto nomem;
+
+		buf = usb_alloc_coherent(dev->udev,
+					 buf_size,
+					 GFP_ATOMIC,
+					 &urb->transfer_dma);
+		if (!buf) {
+			netdev_err(netdev, "No memory left for USB buffer\n");
+			usb_free_urb(urb);
+			goto nomem;
+		}
+		/* Clear first 4 bytes */
+		*(u32 *)buf = 0;
+
+		msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE];
+		fd_msg = &msg->msg.canfd_msg;
+
+		msg->type = CPC_CMD_TYPE_CANFD_FRAME;
+
+		fd_msg->id = cpu_to_le32(cfd->can_id & CAN_ERR_MASK);
+		dlc = cfd->len;
+		fd_msg->length = dlc;
+		fd_msg->flags = 0;
 
-	msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE];
+		if (cfd->can_id & CAN_EFF_FLAG)
+			fd_msg->flags |= CPC_FDFLAG_XTD;
 
-	msg->msg.can_msg.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK);
-	msg->msg.can_msg.length = cf->can_dlc;
+		if (cfd->flags & CANFD_BRS)
+			fd_msg->flags |= CPC_FDFLAG_BRS;
 
-	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;
+		if (cfd->flags & CANFD_ESI)
+			fd_msg->flags |= CPC_FDFLAG_ESI;
 
-		msg->length = CPC_CAN_MSG_MIN_SIZE;
+		memcpy(fd_msg->msg, cfd->data, dlc);
+
+		msg->length = CPC_CANFD_MSG_MIN_SIZE + dlc;
+		/* Send only significant bytes of buffer */
+		buf_len += msg->length;
 	} else {
-		msg->type = cf->can_id & CAN_EFF_FLAG ?
-			CPC_CMD_TYPE_EXT_CAN_FRAME : CPC_CMD_TYPE_CAN_FRAME;
+		struct can_frame *cf = (struct can_frame *)skb->data;
+		struct cpc_can_msg *can_msg;
 
-		for (i = 0; i < cf->can_dlc; i++)
-			msg->msg.can_msg.msg[i] = cf->data[i];
+		buf_size = CPC_HEADER_SIZE +
+			   CPC_MSG_HEADER_LEN +
+			   sizeof(struct cpc_can_msg);
+
+		/* Create an URB and a buffer big enough for
+		 * all message lengths, copy the data to the URB
+		 */
+		urb = usb_alloc_urb(0, GFP_ATOMIC);
+		if (!urb)
+			goto nomem;
+
+		buf = usb_alloc_coherent(dev->udev,
+					 buf_size,
+					 GFP_ATOMIC,
+					 &urb->transfer_dma);
+		if (!buf) {
+			netdev_err(netdev, "No memory left for USB buffer\n");
+			usb_free_urb(urb);
+			goto nomem;
+		}
+		/* Clear first 4 bytes */
+		*(u32 *)buf = 0;
+
+		msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE];
+		can_msg = &msg->msg.can_msg;
+
+		can_msg->id = cpu_to_le32(cf->can_id & CAN_ERR_MASK);
+		dlc = cf->can_dlc;
+		can_msg->length = 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 < dlc; i++)
+				can_msg->msg[i] = cf->data[i];
+
+			msg->length = CPC_CAN_MSG_MIN_SIZE + dlc;
+		}
 
-		msg->length = CPC_CAN_MSG_MIN_SIZE + cf->can_dlc;
+		/* Send only significant bytes of buffer */
+		buf_len += msg->length;
 	}
 
 	for (i = 0; i < MAX_TX_URBS; i++) {
@@ -959,7 +1034,7 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne
 	 * allowed (MAX_TX_URBS).
 	 */
 	if (!context) {
-		usb_free_coherent(dev->udev, size, buf, urb->transfer_dma);
+		usb_free_coherent(dev->udev, buf_size, buf, urb->transfer_dma);
 		usb_free_urb(urb);
 
 		netdev_warn(netdev, "couldn't find free context\n");
@@ -969,10 +1044,10 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne
 
 	context->dev = dev;
 	context->echo_index = i;
-	context->dlc = cf->can_dlc;
+	context->dlc = dlc;
 
 	usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf,
-			  size, ems_usb_write_bulk_callback, context);
+			  buf_len, ems_usb_write_bulk_callback, context);
 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 	usb_anchor_urb(urb, &dev->tx_submitted);
 
@@ -985,7 +1060,7 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne
 		can_free_echo_skb(netdev, context->echo_index);
 
 		usb_unanchor_urb(urb);
-		usb_free_coherent(dev->udev, size, buf, urb->transfer_dma);
+		usb_free_coherent(dev->udev, buf_size, buf, urb->transfer_dma);
 		dev_kfree_skb(skb);
 
 		atomic_dec(&dev->active_tx_urbs);
-- 
2.26.2

-- 
EMS Dr. Thomas Wuensche e.K.
Sonnenhang 3
85304 Ilmmuenster
HR Ingolstadt, HRA 170106

Phone: +49-8441-490260
Fax  : +49-8441-81860
http://www.ems-wuensche.com



[Index of Archives]     [Automotive Discussions]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]     [CAN Bus]

  Powered by Linux