162306a36Sopenharmony_ci/* vcan.c - Virtual CAN interface 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Copyright (c) 2002-2017 Volkswagen Group Electronic Research 462306a36Sopenharmony_ci * All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 762306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 862306a36Sopenharmony_ci * are met: 962306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1062306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1162306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1262306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1362306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 1462306a36Sopenharmony_ci * 3. Neither the name of Volkswagen nor the names of its contributors 1562306a36Sopenharmony_ci * may be used to endorse or promote products derived from this software 1662306a36Sopenharmony_ci * without specific prior written permission. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this 1962306a36Sopenharmony_ci * software may be distributed under the terms of the GNU General 2062306a36Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the 2162306a36Sopenharmony_ci * GPL apply INSTEAD OF those given above. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * The provided data structures and external interfaces from this code 2462306a36Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2762306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2862306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2962306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3062306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3162306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3262306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3362306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3462306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3562306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3662306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 3762306a36Sopenharmony_ci * DAMAGE. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include <linux/ethtool.h> 4462306a36Sopenharmony_ci#include <linux/module.h> 4562306a36Sopenharmony_ci#include <linux/init.h> 4662306a36Sopenharmony_ci#include <linux/netdevice.h> 4762306a36Sopenharmony_ci#include <linux/if_arp.h> 4862306a36Sopenharmony_ci#include <linux/if_ether.h> 4962306a36Sopenharmony_ci#include <linux/can.h> 5062306a36Sopenharmony_ci#include <linux/can/can-ml.h> 5162306a36Sopenharmony_ci#include <linux/can/dev.h> 5262306a36Sopenharmony_ci#include <linux/can/skb.h> 5362306a36Sopenharmony_ci#include <linux/slab.h> 5462306a36Sopenharmony_ci#include <net/rtnetlink.h> 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define DRV_NAME "vcan" 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciMODULE_DESCRIPTION("virtual CAN interface"); 5962306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 6062306a36Sopenharmony_ciMODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>"); 6162306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK(DRV_NAME); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* CAN test feature: 6462306a36Sopenharmony_ci * Enable the echo on driver level for testing the CAN core echo modes. 6562306a36Sopenharmony_ci * See Documentation/networking/can.rst for details. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic bool echo; /* echo testing. Default: 0 (Off) */ 6962306a36Sopenharmony_cimodule_param(echo, bool, 0444); 7062306a36Sopenharmony_ciMODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)"); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void vcan_rx(struct sk_buff *skb, struct net_device *dev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci stats->rx_packets++; 7762306a36Sopenharmony_ci stats->rx_bytes += can_skb_get_data_len(skb); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci skb->pkt_type = PACKET_BROADCAST; 8062306a36Sopenharmony_ci skb->dev = dev; 8162306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci netif_rx(skb); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 8962306a36Sopenharmony_ci unsigned int len; 9062306a36Sopenharmony_ci int loop; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (can_dropped_invalid_skb(dev, skb)) 9362306a36Sopenharmony_ci return NETDEV_TX_OK; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci len = can_skb_get_data_len(skb); 9662306a36Sopenharmony_ci stats->tx_packets++; 9762306a36Sopenharmony_ci stats->tx_bytes += len; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* set flag whether this packet has to be looped back */ 10062306a36Sopenharmony_ci loop = skb->pkt_type == PACKET_LOOPBACK; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci skb_tx_timestamp(skb); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (!echo) { 10562306a36Sopenharmony_ci /* no echo handling available inside this driver */ 10662306a36Sopenharmony_ci if (loop) { 10762306a36Sopenharmony_ci /* only count the packets here, because the 10862306a36Sopenharmony_ci * CAN core already did the echo for us 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci stats->rx_packets++; 11162306a36Sopenharmony_ci stats->rx_bytes += len; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci consume_skb(skb); 11462306a36Sopenharmony_ci return NETDEV_TX_OK; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* perform standard echo handling for CAN network interfaces */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (loop) { 12062306a36Sopenharmony_ci skb = can_create_echo_skb(skb); 12162306a36Sopenharmony_ci if (!skb) 12262306a36Sopenharmony_ci return NETDEV_TX_OK; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* receive with packet counting */ 12562306a36Sopenharmony_ci vcan_rx(skb, dev); 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci /* no looped packets => no counting */ 12862306a36Sopenharmony_ci consume_skb(skb); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci return NETDEV_TX_OK; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int vcan_change_mtu(struct net_device *dev, int new_mtu) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci /* Do not allow changing the MTU while running */ 13662306a36Sopenharmony_ci if (dev->flags & IFF_UP) 13762306a36Sopenharmony_ci return -EBUSY; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU && 14062306a36Sopenharmony_ci !can_is_canxl_dev_mtu(new_mtu)) 14162306a36Sopenharmony_ci return -EINVAL; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci dev->mtu = new_mtu; 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic const struct net_device_ops vcan_netdev_ops = { 14862306a36Sopenharmony_ci .ndo_start_xmit = vcan_tx, 14962306a36Sopenharmony_ci .ndo_change_mtu = vcan_change_mtu, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic const struct ethtool_ops vcan_ethtool_ops = { 15362306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void vcan_setup(struct net_device *dev) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci dev->type = ARPHRD_CAN; 15962306a36Sopenharmony_ci dev->mtu = CANFD_MTU; 16062306a36Sopenharmony_ci dev->hard_header_len = 0; 16162306a36Sopenharmony_ci dev->addr_len = 0; 16262306a36Sopenharmony_ci dev->tx_queue_len = 0; 16362306a36Sopenharmony_ci dev->flags = IFF_NOARP; 16462306a36Sopenharmony_ci can_set_ml_priv(dev, netdev_priv(dev)); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* set flags according to driver capabilities */ 16762306a36Sopenharmony_ci if (echo) 16862306a36Sopenharmony_ci dev->flags |= IFF_ECHO; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci dev->netdev_ops = &vcan_netdev_ops; 17162306a36Sopenharmony_ci dev->ethtool_ops = &vcan_ethtool_ops; 17262306a36Sopenharmony_ci dev->needs_free_netdev = true; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic struct rtnl_link_ops vcan_link_ops __read_mostly = { 17662306a36Sopenharmony_ci .kind = DRV_NAME, 17762306a36Sopenharmony_ci .priv_size = sizeof(struct can_ml_priv), 17862306a36Sopenharmony_ci .setup = vcan_setup, 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic __init int vcan_init_module(void) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci pr_info("Virtual CAN interface driver\n"); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (echo) 18662306a36Sopenharmony_ci pr_info("enabled echo on driver level.\n"); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return rtnl_link_register(&vcan_link_ops); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic __exit void vcan_cleanup_module(void) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci rtnl_link_unregister(&vcan_link_ops); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cimodule_init(vcan_init_module); 19762306a36Sopenharmony_cimodule_exit(vcan_cleanup_module); 198