1/*
2 * Copyright 2018, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
8 * See "LICENSE_GPLv2.txt" for details.
9 *
10 * @TAG(DATA61_GPL)
11 */
12
13$esc:(#include <linux_hdrs.h>)
14$esc:(#include <wrapper.h>)
15#include <abstract.h>
16#include <generated.c>
17
18/* Forward declaration of C functions  */
19static void cg_loopback_setup_ac(struct net_device *dev);
20
21#include <plat/linux/linux_api.ac>
22
23/* The higher levels take care of making this non-reentrant (it's
24 * called with bh's disabled).
25 */
26static netdev_tx_t cg_loopback_xmit(struct sk_buff *skb,
27                                    struct net_device *dev)
28{
29        struct pcpu_lstats *lb_stats;
30        int len;
31
32        skb_tx_timestamp(skb);
33        skb_orphan(skb);
34
35        /* Before queueing this packet to netif_rx(),
36         * make sure dst is refcounted.
37         */
38        skb_dst_force(skb);
39
40        skb->protocol = eth_type_trans(skb, dev);
41
42        /* it's OK to use per_cpu_ptr() because BHs are off */
43        lb_stats = this_cpu_ptr(dev->lstats);
44
45        len = skb->len;
46        if (likely(netif_rx(skb) == NET_RX_SUCCESS)) {
47                u64_stats_update_begin(&lb_stats->syncp);
48                lb_stats->bytes += len;
49                lb_stats->packets++;
50                u64_stats_update_end(&lb_stats->syncp);
51        }
52
53        return NETDEV_TX_OK;
54}
55
56static void cg_loopback_get_stats64(struct net_device *dev,
57                                    struct rtnl_link_stats64 *stats)
58{
59        u64 bytes = 0;
60        u64 packets = 0;
61        int i;
62
63        for (i = -1; i = cpumask_next(i, cpu_possible_mask), i < nr_cpu_ids;) {
64                const struct pcpu_lstats *lb_stats;
65                u64 tbytes, tpackets;
66                unsigned int start;
67
68                lb_stats = per_cpu_ptr(dev->lstats, i);
69                do {
70                        start = u64_stats_fetch_begin_irq(&lb_stats->syncp);
71                        tbytes = lb_stats->bytes;
72                        tpackets = lb_stats->packets;
73                } while (u64_stats_fetch_retry_irq(&lb_stats->syncp, start));
74                bytes   += tbytes;
75                packets += tpackets;
76        }
77        stats->rx_packets = packets;
78        stats->tx_packets = packets;
79        stats->rx_bytes   = bytes;
80        stats->tx_bytes   = bytes;
81}
82
83static u32 cg_always_on_ac(struct net_device *dev)
84{
85        $ty:((NetDeviceAbstractType!, U32)) ret;
86
87        ret = cg_always_on_cg(dev);
88        return ret.p2;
89}
90
91static int cg_loopback_get_ts_info(struct net_device *netdev,
92                                   struct ethtool_ts_info *ts_info)
93{
94        $ty:(()) empty;
95
96        ts_info->so_timestamping = get_ts_info_timestamp_flags_cg(empty);
97        ts_info->phc_index = -1;
98
99        return 0;
100}
101
102static const struct ethtool_ops cg_loopback_ethtool_ops = {
103        .get_link               = cg_always_on_ac,
104        .get_ts_info            = cg_loopback_get_ts_info,
105};
106
107static int cg_loopback_dev_init(struct net_device *dev)
108{
109        SysState state;
110        $ty:((SysState, NetDeviceAbstractType)) args;
111        $ty:(RR (SysState, NetDeviceAbstractType) () ()) ret;
112
113        args.p1 = &state;
114        args.p2 = dev;
115
116        ret = cg_loopback_dev_init_cg(args);
117        if (ret.p2.tag == TAG_ENUM_Success) {
118                return 0;
119        } else {
120                return -ENOMEM;
121        }
122}
123
124static void cg_loopback_dev_free(struct net_device *dev)
125{
126        SysState state;
127        $ty:((SysState, NetDeviceAbstractType)) args;
128
129        args.p1 = &state;
130        args.p2 = dev;
131
132        cg_loopback_dev_free_cg(args);
133}
134
135static const struct net_device_ops cg_loopback_ops = {
136        .ndo_init        = cg_loopback_dev_init,
137        .ndo_start_xmit  = cg_loopback_xmit,
138        .ndo_get_stats64 = cg_loopback_get_stats64,
139        .ndo_set_mac_address = eth_mac_addr,
140};
141
142static void cg_loopback_setup_ac(struct net_device *dev)
143{
144        dev->mtu                = 64 * 1024;
145        dev->hard_header_len    = ETH_HLEN;     /* 14   */
146        dev->min_header_len     = ETH_HLEN;     /* 14   */
147        dev->addr_len           = ETH_ALEN;     /* 6    */
148        dev->type               = ARPHRD_LOOPBACK;      /* 0x0001*/
149        dev->flags              = IFF_LOOPBACK;
150        dev->priv_flags         |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
151        netif_keep_dst(dev);
152        dev->hw_features        = NETIF_F_GSO_SOFTWARE;
153        dev->features           = NETIF_F_SG | NETIF_F_FRAGLIST
154                | NETIF_F_GSO_SOFTWARE
155                | NETIF_F_HW_CSUM
156                | NETIF_F_RXCSUM
157                | NETIF_F_SCTP_CRC
158                | NETIF_F_HIGHDMA
159                | NETIF_F_LLTX
160                | NETIF_F_NETNS_LOCAL
161                | NETIF_F_VLAN_CHALLENGED
162                | NETIF_F_LOOPBACK;
163        dev->ethtool_ops        = &cg_loopback_ethtool_ops;
164        ether_setup(dev);
165        dev->netdev_ops         = &cg_loopback_ops;
166        dev->needs_free_netdev  = true;
167        dev->priv_destructor    = cg_loopback_dev_free;
168}
169
170int cg_loopback_net_init_ac(struct net *net)
171{
172        SysState state;
173        $ty:((SysState, NetAbstractType)) args;
174        $ty:(RR (SysState, NetAbstractType) () ()) ret;
175
176        args.p1 = &state;
177        args.p2 = net;
178
179        ret = cg_loopback_net_init_cg(args);
180        if (ret.p2.tag == TAG_ENUM_Success) {
181                return 0;
182        } else {
183                return -ENOMEM;
184        }
185}
186