123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- /* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- *
- *
- * RMNET Data virtual network driver
- *
- */
- #include <linux/etherdevice.h>
- #include <linux/if_arp.h>
- #include <net/pkt_sched.h>
- #include "rmnet_config.h"
- #include "rmnet_handlers.h"
- #include "rmnet_private.h"
- #include "rmnet_map.h"
- #include "rmnet_vnd.h"
- /* RX/TX Fixup */
- void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)
- {
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += skb->len;
- }
- void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)
- {
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
- }
- /* Network Device Operations */
- static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
- {
- struct rmnet_priv *priv;
- priv = netdev_priv(dev);
- if (priv->local_ep.egress_dev) {
- rmnet_egress_handler(skb, &priv->local_ep);
- } else {
- dev->stats.tx_dropped++;
- kfree_skb(skb);
- }
- return NETDEV_TX_OK;
- }
- static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
- {
- if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)
- return -EINVAL;
- rmnet_dev->mtu = new_mtu;
- return 0;
- }
- static int rmnet_vnd_get_iflink(const struct net_device *dev)
- {
- struct rmnet_priv *priv = netdev_priv(dev);
- return priv->real_dev->ifindex;
- }
- static const struct net_device_ops rmnet_vnd_ops = {
- .ndo_start_xmit = rmnet_vnd_start_xmit,
- .ndo_change_mtu = rmnet_vnd_change_mtu,
- .ndo_get_iflink = rmnet_vnd_get_iflink,
- };
- /* Called by kernel whenever a new rmnet<n> device is created. Sets MTU,
- * flags, ARP type, needed headroom, etc...
- */
- void rmnet_vnd_setup(struct net_device *rmnet_dev)
- {
- rmnet_dev->netdev_ops = &rmnet_vnd_ops;
- rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE;
- rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM;
- random_ether_addr(rmnet_dev->dev_addr);
- rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN;
- /* Raw IP mode */
- rmnet_dev->header_ops = NULL; /* No header */
- rmnet_dev->type = ARPHRD_RAWIP;
- rmnet_dev->hard_header_len = 0;
- rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
- rmnet_dev->needs_free_netdev = true;
- }
- /* Exposed API */
- int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
- struct rmnet_port *port,
- struct net_device *real_dev)
- {
- struct rmnet_priv *priv;
- int rc;
- if (port->rmnet_devices[id])
- return -EINVAL;
- rc = register_netdevice(rmnet_dev);
- if (!rc) {
- port->rmnet_devices[id] = rmnet_dev;
- port->nr_rmnet_devs++;
- rmnet_dev->rtnl_link_ops = &rmnet_link_ops;
- priv = netdev_priv(rmnet_dev);
- priv->mux_id = id;
- priv->real_dev = real_dev;
- netdev_dbg(rmnet_dev, "rmnet dev created\n");
- }
- return rc;
- }
- int rmnet_vnd_dellink(u8 id, struct rmnet_port *port)
- {
- if (id >= RMNET_MAX_LOGICAL_EP || !port->rmnet_devices[id])
- return -EINVAL;
- port->rmnet_devices[id] = NULL;
- port->nr_rmnet_devs--;
- return 0;
- }
- u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev)
- {
- struct rmnet_priv *priv;
- priv = netdev_priv(rmnet_dev);
- return priv->mux_id;
- }
- /* Gets the logical endpoint configuration for a RmNet virtual network device
- * node. Caller should confirm that devices is a RmNet VND before calling.
- */
- struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *rmnet_dev)
- {
- struct rmnet_priv *priv;
- if (!rmnet_dev)
- return NULL;
- priv = netdev_priv(rmnet_dev);
- return &priv->local_ep;
- }
- int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable)
- {
- netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable);
- /* Although we expect similar number of enable/disable
- * commands, optimize for the disable. That is more
- * latency sensitive than enable
- */
- if (unlikely(enable))
- netif_wake_queue(rmnet_dev);
- else
- netif_stop_queue(rmnet_dev);
- return 0;
- }
|