diff options
Diffstat (limited to 'drivers/net/can/usb/mcba_usb.c')
| -rw-r--r-- | drivers/net/can/usb/mcba_usb.c | 131 |
1 files changed, 69 insertions, 62 deletions
diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c index 8d8c2086424d..41c0a1c399bf 100644 --- a/drivers/net/can/usb/mcba_usb.c +++ b/drivers/net/can/usb/mcba_usb.c @@ -1,27 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /* SocketCAN driver for Microchip CAN BUS Analyzer Tool * * Copyright (C) 2017 Mobica Limited * - * 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. - * * This driver is inspired by the 4.6.2 version of net/can/usb/usb_8dev.c */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/error.h> -#include <linux/can/led.h> +#include <linux/ethtool.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/signal.h> @@ -39,15 +28,11 @@ #define MCBA_CTX_FREE MCBA_MAX_TX_URBS /* RX buffer must be bigger than msg size since at the - * beggining USB messages are stacked. + * beginning USB messages are stacked. */ #define MCBA_USB_RX_BUFF_SIZE 64 #define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg)) -/* MCBA endpoint numbers */ -#define MCBA_USB_EP_IN 1 -#define MCBA_USB_EP_OUT 1 - /* Microchip command id */ #define MBCA_CMD_RECEIVE_MESSAGE 0xE3 #define MBCA_CMD_I_AM_ALIVE_FROM_CAN 0xF5 @@ -62,6 +47,10 @@ #define MCBA_VER_REQ_USB 1 #define MCBA_VER_REQ_CAN 2 +/* Drive the CAN_RES signal LOW "0" to activate R24 and R25 */ +#define MCBA_VER_TERMINATION_ON 0 +#define MCBA_VER_TERMINATION_OFF 1 + #define MCBA_SIDL_EXID_MASK 0x8 #define MCBA_DLC_MASK 0xf #define MCBA_DLC_RTR_MASK 0x40 @@ -75,7 +64,6 @@ struct mcba_usb_ctx { struct mcba_priv *priv; u32 ndx; - u8 dlc; bool can; }; @@ -93,6 +81,10 @@ struct mcba_priv { bool can_ka_first_pass; bool can_speed_check; atomic_t free_ctx_cnt; + void *rxbuf[MCBA_MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS]; + int rx_pipe; + int tx_pipe; }; /* CAN frame */ @@ -193,13 +185,10 @@ static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv, ctx = &priv->tx_context[i]; ctx->ndx = i; - if (cf) { + if (cf) ctx->can = true; - ctx->dlc = cf->can_dlc; - } else { + else ctx->can = false; - ctx->dlc = 0; - } atomic_dec(&priv->free_ctx_cnt); break; @@ -245,10 +234,8 @@ static void mcba_usb_write_bulk_callback(struct urb *urb) return; netdev->stats.tx_packets++; - netdev->stats.tx_bytes += ctx->dlc; - - can_led_event(netdev, CAN_LED_EVENT_TX); - can_get_echo_skb(netdev, ctx->ndx); + netdev->stats.tx_bytes += can_get_echo_skb(netdev, ctx->ndx, + NULL); } if (urb->status) @@ -281,10 +268,8 @@ static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv, memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE); - usb_fill_bulk_urb(urb, priv->udev, - usb_sndbulkpipe(priv->udev, MCBA_USB_EP_OUT), buf, - MCBA_USB_TX_BUFF_SIZE, mcba_usb_write_bulk_callback, - ctx); + usb_fill_bulk_urb(urb, priv->udev, priv->tx_pipe, buf, MCBA_USB_TX_BUFF_SIZE, + mcba_usb_write_bulk_callback, ctx); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &priv->tx_submitted); @@ -330,15 +315,13 @@ static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb, .cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV }; - if (can_dropped_invalid_skb(netdev, skb)) + if (can_dev_dropped_skb(netdev, skb)) return NETDEV_TX_OK; ctx = mcba_usb_get_free_ctx(priv, cf); if (!ctx) return NETDEV_TX_BUSY; - can_put_echo_skb(skb, priv->netdev, ctx->ndx); - if (cf->can_id & CAN_EFF_FLAG) { /* SIDH | SIDL | EIDH | EIDL * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0 @@ -361,13 +344,15 @@ static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb, usb_msg.eid = 0; } - usb_msg.dlc = cf->can_dlc; + usb_msg.dlc = cf->len; memcpy(usb_msg.data, cf->data, usb_msg.dlc); if (cf->can_id & CAN_RTR_FLAG) usb_msg.dlc |= MCBA_DLC_RTR_MASK; + can_put_echo_skb(skb, priv->netdev, ctx->ndx, 0); + err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx); if (err) goto xmit_failed; @@ -375,9 +360,8 @@ static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; xmit_failed: - can_free_echo_skb(priv->netdev, ctx->ndx); + can_free_echo_skb(priv->netdev, ctx->ndx, NULL); mcba_usb_free_ctx(ctx); - dev_kfree_skb(skb); stats->tx_dropped++; return NETDEV_TX_OK; @@ -459,17 +443,17 @@ static void mcba_usb_process_can(struct mcba_priv *priv, cf->can_id = (sid & 0xffe0) >> 5; } - if (msg->dlc & MCBA_DLC_RTR_MASK) - cf->can_id |= CAN_RTR_FLAG; - - cf->can_dlc = get_can_dlc(msg->dlc & MCBA_DLC_MASK); + cf->len = can_cc_dlc2len(msg->dlc & MCBA_DLC_MASK); - memcpy(cf->data, msg->data, cf->can_dlc); + if (msg->dlc & MCBA_DLC_RTR_MASK) { + cf->can_id |= CAN_RTR_FLAG; + } else { + memcpy(cf->data, msg->data, cf->len); + stats->rx_bytes += cf->len; + } stats->rx_packets++; - stats->rx_bytes += cf->can_dlc; - can_led_event(priv->netdev, CAN_LED_EVENT_RX); netif_rx(skb); } @@ -477,13 +461,13 @@ static void mcba_usb_process_ka_usb(struct mcba_priv *priv, struct mcba_usb_msg_ka_usb *msg) { if (unlikely(priv->usb_ka_first_pass)) { - netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n", + netdev_info(priv->netdev, "PIC USB version %u.%u\n", msg->soft_ver_major, msg->soft_ver_minor); priv->usb_ka_first_pass = false; } - if (msg->termination_state) + if (msg->termination_state == MCBA_VER_TERMINATION_ON) priv->can.termination = MCBA_TERMINATION_ENABLED; else priv->can.termination = MCBA_TERMINATION_DISABLED; @@ -503,7 +487,7 @@ static void mcba_usb_process_ka_can(struct mcba_priv *priv, struct mcba_usb_msg_ka_can *msg) { if (unlikely(priv->can_ka_first_pass)) { - netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n", + netdev_info(priv->netdev, "PIC CAN version %u.%u\n", msg->soft_ver_major, msg->soft_ver_minor); priv->can_ka_first_pass = false; @@ -565,7 +549,7 @@ static void mcba_usb_process_rx(struct mcba_priv *priv, break; default: - netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)", + netdev_warn(priv->netdev, "Unsupported msg (0x%X)", msg->cmd_id); break; } @@ -620,7 +604,7 @@ static void mcba_usb_read_bulk_callback(struct urb *urb) resubmit_urb: usb_fill_bulk_urb(urb, priv->udev, - usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_OUT), + priv->rx_pipe, urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE, mcba_usb_read_bulk_callback, priv); @@ -644,6 +628,7 @@ static int mcba_usb_start(struct mcba_priv *priv) for (i = 0; i < MCBA_MAX_RX_URBS; i++) { struct urb *urb = NULL; u8 *buf; + dma_addr_t buf_dma; /* create a URB, and a buffer for it */ urb = usb_alloc_urb(0, GFP_KERNEL); @@ -653,7 +638,7 @@ static int mcba_usb_start(struct mcba_priv *priv) } buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, - GFP_KERNEL, &urb->transfer_dma); + GFP_KERNEL, &buf_dma); if (!buf) { netdev_err(netdev, "No memory left for USB buffer\n"); usb_free_urb(urb); @@ -661,8 +646,10 @@ static int mcba_usb_start(struct mcba_priv *priv) break; } + urb->transfer_dma = buf_dma; + usb_fill_bulk_urb(urb, priv->udev, - usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_IN), + priv->rx_pipe, buf, MCBA_USB_RX_BUFF_SIZE, mcba_usb_read_bulk_callback, priv); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -672,11 +659,14 @@ static int mcba_usb_start(struct mcba_priv *priv) if (err) { usb_unanchor_urb(urb); usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, - buf, urb->transfer_dma); + buf, buf_dma); usb_free_urb(urb); break; } + priv->rxbuf[i] = buf; + priv->rxbuf_dma[i] = buf_dma; + /* Drop reference, USB core will take care of freeing it */ usb_free_urb(urb); } @@ -711,7 +701,6 @@ static int mcba_usb_open(struct net_device *netdev) priv->can_speed_check = true; priv->can.state = CAN_STATE_ERROR_ACTIVE; - can_led_event(netdev, CAN_LED_EVENT_OPEN); netif_start_queue(netdev); return 0; @@ -719,7 +708,14 @@ static int mcba_usb_open(struct net_device *netdev) static void mcba_urb_unlink(struct mcba_priv *priv) { + int i; + usb_kill_anchored_urbs(&priv->rx_submitted); + + for (i = 0; i < MCBA_MAX_RX_URBS; ++i) + usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + priv->rxbuf[i], priv->rxbuf_dma[i]); + usb_kill_anchored_urbs(&priv->tx_submitted); } @@ -736,7 +732,6 @@ static int mcba_usb_close(struct net_device *netdev) mcba_urb_unlink(priv); close_candev(netdev); - can_led_event(netdev, CAN_LED_EVENT_STOP); return 0; } @@ -768,6 +763,10 @@ static const struct net_device_ops mcba_netdev_ops = { .ndo_start_xmit = mcba_usb_start_xmit, }; +static const struct ethtool_ops mcba_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, +}; + /* Microchip CANBUS has hardcoded bittiming values by default. * This function sends request via USB to change the speed and align bittiming * values for presentation purposes only @@ -790,9 +789,9 @@ static int mcba_set_termination(struct net_device *netdev, u16 term) }; if (term == MCBA_TERMINATION_ENABLED) - usb_msg.termination = 1; + usb_msg.termination = MCBA_VER_TERMINATION_ON; else - usb_msg.termination = 0; + usb_msg.termination = MCBA_VER_TERMINATION_OFF; mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); @@ -804,8 +803,15 @@ static int mcba_usb_probe(struct usb_interface *intf, { struct net_device *netdev; struct mcba_priv *priv; - int err = -ENOMEM; + int err; struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *in, *out; + + err = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL, NULL); + if (err) { + dev_err(&intf->dev, "Can't find endpoints\n"); + return err; + } netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS); if (!netdev) { @@ -839,6 +845,7 @@ static int mcba_usb_probe(struct usb_interface *intf, priv->can.do_set_bittiming = mcba_net_set_bittiming; netdev->netdev_ops = &mcba_netdev_ops; + netdev->ethtool_ops = &mcba_ethtool_ops; netdev->flags |= IFF_ECHO; /* we support local echo */ @@ -851,7 +858,8 @@ static int mcba_usb_probe(struct usb_interface *intf, goto cleanup_free_candev; } - devm_can_led_init(netdev); + priv->rx_pipe = usb_rcvbulkpipe(priv->udev, in->bEndpointAddress); + priv->tx_pipe = usb_sndbulkpipe(priv->udev, out->bEndpointAddress); /* Start USB dev only if we have successfully registered CAN device */ err = mcba_usb_start(priv); @@ -887,9 +895,8 @@ static void mcba_usb_disconnect(struct usb_interface *intf) netdev_info(priv->netdev, "device disconnected\n"); unregister_candev(priv->netdev); - free_candev(priv->netdev); - mcba_urb_unlink(priv); + free_candev(priv->netdev); } static struct usb_driver mcba_usb_driver = { |
