162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2019 Synopsys, Inc. and/or its affiliates. 462306a36Sopenharmony_ci * stmmac Selftests Support 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Jose Abreu <joabreu@synopsys.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Ported from stmmac by: 962306a36Sopenharmony_ci * Copyright (C) 2021 Oleksij Rempel <o.rempel@pengutronix.de> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/phy.h> 1362306a36Sopenharmony_ci#include <net/selftests.h> 1462306a36Sopenharmony_ci#include <net/tcp.h> 1562306a36Sopenharmony_ci#include <net/udp.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct net_packet_attrs { 1862306a36Sopenharmony_ci const unsigned char *src; 1962306a36Sopenharmony_ci const unsigned char *dst; 2062306a36Sopenharmony_ci u32 ip_src; 2162306a36Sopenharmony_ci u32 ip_dst; 2262306a36Sopenharmony_ci bool tcp; 2362306a36Sopenharmony_ci u16 sport; 2462306a36Sopenharmony_ci u16 dport; 2562306a36Sopenharmony_ci int timeout; 2662306a36Sopenharmony_ci int size; 2762306a36Sopenharmony_ci int max_size; 2862306a36Sopenharmony_ci u8 id; 2962306a36Sopenharmony_ci u16 queue_mapping; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct net_test_priv { 3362306a36Sopenharmony_ci struct net_packet_attrs *packet; 3462306a36Sopenharmony_ci struct packet_type pt; 3562306a36Sopenharmony_ci struct completion comp; 3662306a36Sopenharmony_ci int double_vlan; 3762306a36Sopenharmony_ci int vlan_id; 3862306a36Sopenharmony_ci int ok; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct netsfhdr { 4262306a36Sopenharmony_ci __be32 version; 4362306a36Sopenharmony_ci __be64 magic; 4462306a36Sopenharmony_ci u8 id; 4562306a36Sopenharmony_ci} __packed; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic u8 net_test_next_id; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define NET_TEST_PKT_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + \ 5062306a36Sopenharmony_ci sizeof(struct netsfhdr)) 5162306a36Sopenharmony_ci#define NET_TEST_PKT_MAGIC 0xdeadcafecafedeadULL 5262306a36Sopenharmony_ci#define NET_LB_TIMEOUT msecs_to_jiffies(200) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic struct sk_buff *net_test_get_skb(struct net_device *ndev, 5562306a36Sopenharmony_ci struct net_packet_attrs *attr) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct sk_buff *skb = NULL; 5862306a36Sopenharmony_ci struct udphdr *uhdr = NULL; 5962306a36Sopenharmony_ci struct tcphdr *thdr = NULL; 6062306a36Sopenharmony_ci struct netsfhdr *shdr; 6162306a36Sopenharmony_ci struct ethhdr *ehdr; 6262306a36Sopenharmony_ci struct iphdr *ihdr; 6362306a36Sopenharmony_ci int iplen, size; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci size = attr->size + NET_TEST_PKT_SIZE; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (attr->tcp) 6862306a36Sopenharmony_ci size += sizeof(struct tcphdr); 6962306a36Sopenharmony_ci else 7062306a36Sopenharmony_ci size += sizeof(struct udphdr); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (attr->max_size && attr->max_size > size) 7362306a36Sopenharmony_ci size = attr->max_size; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci skb = netdev_alloc_skb(ndev, size); 7662306a36Sopenharmony_ci if (!skb) 7762306a36Sopenharmony_ci return NULL; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci prefetchw(skb->data); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ehdr = skb_push(skb, ETH_HLEN); 8262306a36Sopenharmony_ci skb_reset_mac_header(skb); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci skb_set_network_header(skb, skb->len); 8562306a36Sopenharmony_ci ihdr = skb_put(skb, sizeof(*ihdr)); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci skb_set_transport_header(skb, skb->len); 8862306a36Sopenharmony_ci if (attr->tcp) 8962306a36Sopenharmony_ci thdr = skb_put(skb, sizeof(*thdr)); 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci uhdr = skb_put(skb, sizeof(*uhdr)); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci eth_zero_addr(ehdr->h_dest); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (attr->src) 9662306a36Sopenharmony_ci ether_addr_copy(ehdr->h_source, attr->src); 9762306a36Sopenharmony_ci if (attr->dst) 9862306a36Sopenharmony_ci ether_addr_copy(ehdr->h_dest, attr->dst); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci ehdr->h_proto = htons(ETH_P_IP); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (attr->tcp) { 10362306a36Sopenharmony_ci thdr->source = htons(attr->sport); 10462306a36Sopenharmony_ci thdr->dest = htons(attr->dport); 10562306a36Sopenharmony_ci thdr->doff = sizeof(struct tcphdr) / 4; 10662306a36Sopenharmony_ci thdr->check = 0; 10762306a36Sopenharmony_ci } else { 10862306a36Sopenharmony_ci uhdr->source = htons(attr->sport); 10962306a36Sopenharmony_ci uhdr->dest = htons(attr->dport); 11062306a36Sopenharmony_ci uhdr->len = htons(sizeof(*shdr) + sizeof(*uhdr) + attr->size); 11162306a36Sopenharmony_ci if (attr->max_size) 11262306a36Sopenharmony_ci uhdr->len = htons(attr->max_size - 11362306a36Sopenharmony_ci (sizeof(*ihdr) + sizeof(*ehdr))); 11462306a36Sopenharmony_ci uhdr->check = 0; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ihdr->ihl = 5; 11862306a36Sopenharmony_ci ihdr->ttl = 32; 11962306a36Sopenharmony_ci ihdr->version = 4; 12062306a36Sopenharmony_ci if (attr->tcp) 12162306a36Sopenharmony_ci ihdr->protocol = IPPROTO_TCP; 12262306a36Sopenharmony_ci else 12362306a36Sopenharmony_ci ihdr->protocol = IPPROTO_UDP; 12462306a36Sopenharmony_ci iplen = sizeof(*ihdr) + sizeof(*shdr) + attr->size; 12562306a36Sopenharmony_ci if (attr->tcp) 12662306a36Sopenharmony_ci iplen += sizeof(*thdr); 12762306a36Sopenharmony_ci else 12862306a36Sopenharmony_ci iplen += sizeof(*uhdr); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (attr->max_size) 13162306a36Sopenharmony_ci iplen = attr->max_size - sizeof(*ehdr); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ihdr->tot_len = htons(iplen); 13462306a36Sopenharmony_ci ihdr->frag_off = 0; 13562306a36Sopenharmony_ci ihdr->saddr = htonl(attr->ip_src); 13662306a36Sopenharmony_ci ihdr->daddr = htonl(attr->ip_dst); 13762306a36Sopenharmony_ci ihdr->tos = 0; 13862306a36Sopenharmony_ci ihdr->id = 0; 13962306a36Sopenharmony_ci ip_send_check(ihdr); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci shdr = skb_put(skb, sizeof(*shdr)); 14262306a36Sopenharmony_ci shdr->version = 0; 14362306a36Sopenharmony_ci shdr->magic = cpu_to_be64(NET_TEST_PKT_MAGIC); 14462306a36Sopenharmony_ci attr->id = net_test_next_id; 14562306a36Sopenharmony_ci shdr->id = net_test_next_id++; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (attr->size) 14862306a36Sopenharmony_ci skb_put(skb, attr->size); 14962306a36Sopenharmony_ci if (attr->max_size && attr->max_size > skb->len) 15062306a36Sopenharmony_ci skb_put(skb, attr->max_size - skb->len); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci skb->csum = 0; 15362306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_PARTIAL; 15462306a36Sopenharmony_ci if (attr->tcp) { 15562306a36Sopenharmony_ci thdr->check = ~tcp_v4_check(skb->len, ihdr->saddr, 15662306a36Sopenharmony_ci ihdr->daddr, 0); 15762306a36Sopenharmony_ci skb->csum_start = skb_transport_header(skb) - skb->head; 15862306a36Sopenharmony_ci skb->csum_offset = offsetof(struct tcphdr, check); 15962306a36Sopenharmony_ci } else { 16062306a36Sopenharmony_ci udp4_hwcsum(skb, ihdr->saddr, ihdr->daddr); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 16462306a36Sopenharmony_ci skb->pkt_type = PACKET_HOST; 16562306a36Sopenharmony_ci skb->dev = ndev; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return skb; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int net_test_loopback_validate(struct sk_buff *skb, 17162306a36Sopenharmony_ci struct net_device *ndev, 17262306a36Sopenharmony_ci struct packet_type *pt, 17362306a36Sopenharmony_ci struct net_device *orig_ndev) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct net_test_priv *tpriv = pt->af_packet_priv; 17662306a36Sopenharmony_ci const unsigned char *src = tpriv->packet->src; 17762306a36Sopenharmony_ci const unsigned char *dst = tpriv->packet->dst; 17862306a36Sopenharmony_ci struct netsfhdr *shdr; 17962306a36Sopenharmony_ci struct ethhdr *ehdr; 18062306a36Sopenharmony_ci struct udphdr *uhdr; 18162306a36Sopenharmony_ci struct tcphdr *thdr; 18262306a36Sopenharmony_ci struct iphdr *ihdr; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci skb = skb_unshare(skb, GFP_ATOMIC); 18562306a36Sopenharmony_ci if (!skb) 18662306a36Sopenharmony_ci goto out; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (skb_linearize(skb)) 18962306a36Sopenharmony_ci goto out; 19062306a36Sopenharmony_ci if (skb_headlen(skb) < (NET_TEST_PKT_SIZE - ETH_HLEN)) 19162306a36Sopenharmony_ci goto out; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ehdr = (struct ethhdr *)skb_mac_header(skb); 19462306a36Sopenharmony_ci if (dst) { 19562306a36Sopenharmony_ci if (!ether_addr_equal_unaligned(ehdr->h_dest, dst)) 19662306a36Sopenharmony_ci goto out; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (src) { 20062306a36Sopenharmony_ci if (!ether_addr_equal_unaligned(ehdr->h_source, src)) 20162306a36Sopenharmony_ci goto out; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ihdr = ip_hdr(skb); 20562306a36Sopenharmony_ci if (tpriv->double_vlan) 20662306a36Sopenharmony_ci ihdr = (struct iphdr *)(skb_network_header(skb) + 4); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (tpriv->packet->tcp) { 20962306a36Sopenharmony_ci if (ihdr->protocol != IPPROTO_TCP) 21062306a36Sopenharmony_ci goto out; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci thdr = (struct tcphdr *)((u8 *)ihdr + 4 * ihdr->ihl); 21362306a36Sopenharmony_ci if (thdr->dest != htons(tpriv->packet->dport)) 21462306a36Sopenharmony_ci goto out; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci shdr = (struct netsfhdr *)((u8 *)thdr + sizeof(*thdr)); 21762306a36Sopenharmony_ci } else { 21862306a36Sopenharmony_ci if (ihdr->protocol != IPPROTO_UDP) 21962306a36Sopenharmony_ci goto out; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci uhdr = (struct udphdr *)((u8 *)ihdr + 4 * ihdr->ihl); 22262306a36Sopenharmony_ci if (uhdr->dest != htons(tpriv->packet->dport)) 22362306a36Sopenharmony_ci goto out; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci shdr = (struct netsfhdr *)((u8 *)uhdr + sizeof(*uhdr)); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (shdr->magic != cpu_to_be64(NET_TEST_PKT_MAGIC)) 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci if (tpriv->packet->id != shdr->id) 23162306a36Sopenharmony_ci goto out; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci tpriv->ok = true; 23462306a36Sopenharmony_ci complete(&tpriv->comp); 23562306a36Sopenharmony_ciout: 23662306a36Sopenharmony_ci kfree_skb(skb); 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int __net_test_loopback(struct net_device *ndev, 24162306a36Sopenharmony_ci struct net_packet_attrs *attr) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct net_test_priv *tpriv; 24462306a36Sopenharmony_ci struct sk_buff *skb = NULL; 24562306a36Sopenharmony_ci int ret = 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL); 24862306a36Sopenharmony_ci if (!tpriv) 24962306a36Sopenharmony_ci return -ENOMEM; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci tpriv->ok = false; 25262306a36Sopenharmony_ci init_completion(&tpriv->comp); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci tpriv->pt.type = htons(ETH_P_IP); 25562306a36Sopenharmony_ci tpriv->pt.func = net_test_loopback_validate; 25662306a36Sopenharmony_ci tpriv->pt.dev = ndev; 25762306a36Sopenharmony_ci tpriv->pt.af_packet_priv = tpriv; 25862306a36Sopenharmony_ci tpriv->packet = attr; 25962306a36Sopenharmony_ci dev_add_pack(&tpriv->pt); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci skb = net_test_get_skb(ndev, attr); 26262306a36Sopenharmony_ci if (!skb) { 26362306a36Sopenharmony_ci ret = -ENOMEM; 26462306a36Sopenharmony_ci goto cleanup; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ret = dev_direct_xmit(skb, attr->queue_mapping); 26862306a36Sopenharmony_ci if (ret < 0) { 26962306a36Sopenharmony_ci goto cleanup; 27062306a36Sopenharmony_ci } else if (ret > 0) { 27162306a36Sopenharmony_ci ret = -ENETUNREACH; 27262306a36Sopenharmony_ci goto cleanup; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (!attr->timeout) 27662306a36Sopenharmony_ci attr->timeout = NET_LB_TIMEOUT; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci wait_for_completion_timeout(&tpriv->comp, attr->timeout); 27962306a36Sopenharmony_ci ret = tpriv->ok ? 0 : -ETIMEDOUT; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cicleanup: 28262306a36Sopenharmony_ci dev_remove_pack(&tpriv->pt); 28362306a36Sopenharmony_ci kfree(tpriv); 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int net_test_netif_carrier(struct net_device *ndev) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci return netif_carrier_ok(ndev) ? 0 : -ENOLINK; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int net_test_phy_phydev(struct net_device *ndev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci return ndev->phydev ? 0 : -EOPNOTSUPP; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic int net_test_phy_loopback_enable(struct net_device *ndev) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci if (!ndev->phydev) 30062306a36Sopenharmony_ci return -EOPNOTSUPP; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return phy_loopback(ndev->phydev, true); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int net_test_phy_loopback_disable(struct net_device *ndev) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci if (!ndev->phydev) 30862306a36Sopenharmony_ci return -EOPNOTSUPP; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return phy_loopback(ndev->phydev, false); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int net_test_phy_loopback_udp(struct net_device *ndev) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct net_packet_attrs attr = { }; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci attr.dst = ndev->dev_addr; 31862306a36Sopenharmony_ci return __net_test_loopback(ndev, &attr); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int net_test_phy_loopback_udp_mtu(struct net_device *ndev) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct net_packet_attrs attr = { }; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci attr.dst = ndev->dev_addr; 32662306a36Sopenharmony_ci attr.max_size = ndev->mtu; 32762306a36Sopenharmony_ci return __net_test_loopback(ndev, &attr); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int net_test_phy_loopback_tcp(struct net_device *ndev) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct net_packet_attrs attr = { }; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci attr.dst = ndev->dev_addr; 33562306a36Sopenharmony_ci attr.tcp = true; 33662306a36Sopenharmony_ci return __net_test_loopback(ndev, &attr); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic const struct net_test { 34062306a36Sopenharmony_ci char name[ETH_GSTRING_LEN]; 34162306a36Sopenharmony_ci int (*fn)(struct net_device *ndev); 34262306a36Sopenharmony_ci} net_selftests[] = { 34362306a36Sopenharmony_ci { 34462306a36Sopenharmony_ci .name = "Carrier ", 34562306a36Sopenharmony_ci .fn = net_test_netif_carrier, 34662306a36Sopenharmony_ci }, { 34762306a36Sopenharmony_ci .name = "PHY dev is present ", 34862306a36Sopenharmony_ci .fn = net_test_phy_phydev, 34962306a36Sopenharmony_ci }, { 35062306a36Sopenharmony_ci /* This test should be done before all PHY loopback test */ 35162306a36Sopenharmony_ci .name = "PHY internal loopback, enable ", 35262306a36Sopenharmony_ci .fn = net_test_phy_loopback_enable, 35362306a36Sopenharmony_ci }, { 35462306a36Sopenharmony_ci .name = "PHY internal loopback, UDP ", 35562306a36Sopenharmony_ci .fn = net_test_phy_loopback_udp, 35662306a36Sopenharmony_ci }, { 35762306a36Sopenharmony_ci .name = "PHY internal loopback, MTU ", 35862306a36Sopenharmony_ci .fn = net_test_phy_loopback_udp_mtu, 35962306a36Sopenharmony_ci }, { 36062306a36Sopenharmony_ci .name = "PHY internal loopback, TCP ", 36162306a36Sopenharmony_ci .fn = net_test_phy_loopback_tcp, 36262306a36Sopenharmony_ci }, { 36362306a36Sopenharmony_ci /* This test should be done after all PHY loopback test */ 36462306a36Sopenharmony_ci .name = "PHY internal loopback, disable", 36562306a36Sopenharmony_ci .fn = net_test_phy_loopback_disable, 36662306a36Sopenharmony_ci }, 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_civoid net_selftest(struct net_device *ndev, struct ethtool_test *etest, u64 *buf) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int count = net_selftest_get_count(); 37262306a36Sopenharmony_ci int i; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci memset(buf, 0, sizeof(*buf) * count); 37562306a36Sopenharmony_ci net_test_next_id = 0; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (etest->flags != ETH_TEST_FL_OFFLINE) { 37862306a36Sopenharmony_ci netdev_err(ndev, "Only offline tests are supported\n"); 37962306a36Sopenharmony_ci etest->flags |= ETH_TEST_FL_FAILED; 38062306a36Sopenharmony_ci return; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci for (i = 0; i < count; i++) { 38562306a36Sopenharmony_ci buf[i] = net_selftests[i].fn(ndev); 38662306a36Sopenharmony_ci if (buf[i] && (buf[i] != -EOPNOTSUPP)) 38762306a36Sopenharmony_ci etest->flags |= ETH_TEST_FL_FAILED; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(net_selftest); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciint net_selftest_get_count(void) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci return ARRAY_SIZE(net_selftests); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(net_selftest_get_count); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_civoid net_selftest_get_strings(u8 *data) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci u8 *p = data; 40162306a36Sopenharmony_ci int i; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci for (i = 0; i < net_selftest_get_count(); i++) { 40462306a36Sopenharmony_ci snprintf(p, ETH_GSTRING_LEN, "%2d. %s", i + 1, 40562306a36Sopenharmony_ci net_selftests[i].name); 40662306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(net_selftest_get_strings); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 41262306a36Sopenharmony_ciMODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); 413