162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * arch/xtensa/platforms/iss/network.c
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Platform specific initialization.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Authors: Chris Zankel <chris@zankel.net>
962306a36Sopenharmony_ci * Based on work form the UML team.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright 2005 Tensilica Inc.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/list.h>
1762306a36Sopenharmony_ci#include <linux/irq.h>
1862306a36Sopenharmony_ci#include <linux/spinlock.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/timer.h>
2162306a36Sopenharmony_ci#include <linux/if_ether.h>
2262306a36Sopenharmony_ci#include <linux/inetdevice.h>
2362306a36Sopenharmony_ci#include <linux/init.h>
2462306a36Sopenharmony_ci#include <linux/if_tun.h>
2562306a36Sopenharmony_ci#include <linux/etherdevice.h>
2662306a36Sopenharmony_ci#include <linux/interrupt.h>
2762306a36Sopenharmony_ci#include <linux/ioctl.h>
2862306a36Sopenharmony_ci#include <linux/memblock.h>
2962306a36Sopenharmony_ci#include <linux/ethtool.h>
3062306a36Sopenharmony_ci#include <linux/rtnetlink.h>
3162306a36Sopenharmony_ci#include <linux/platform_device.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <platform/simcall.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define DRIVER_NAME "iss-netdev"
3662306a36Sopenharmony_ci#define ETH_MAX_PACKET 1500
3762306a36Sopenharmony_ci#define ETH_HEADER_OTHER 14
3862306a36Sopenharmony_ci#define ISS_NET_TIMER_VALUE (HZ / 10)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* We currently only support the TUNTAP transport protocol. */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define TRANSPORT_TUNTAP_NAME "tuntap"
4562306a36Sopenharmony_ci#define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct tuntap_info {
4862306a36Sopenharmony_ci	char dev_name[IFNAMSIZ];
4962306a36Sopenharmony_ci	int fd;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct iss_net_private;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct iss_net_ops {
5862306a36Sopenharmony_ci	int (*open)(struct iss_net_private *lp);
5962306a36Sopenharmony_ci	void (*close)(struct iss_net_private *lp);
6062306a36Sopenharmony_ci	int (*read)(struct iss_net_private *lp, struct sk_buff **skb);
6162306a36Sopenharmony_ci	int (*write)(struct iss_net_private *lp, struct sk_buff **skb);
6262306a36Sopenharmony_ci	unsigned short (*protocol)(struct sk_buff *skb);
6362306a36Sopenharmony_ci	int (*poll)(struct iss_net_private *lp);
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* This structure contains out private information for the driver. */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct iss_net_private {
6962306a36Sopenharmony_ci	spinlock_t lock;
7062306a36Sopenharmony_ci	struct net_device *dev;
7162306a36Sopenharmony_ci	struct platform_device pdev;
7262306a36Sopenharmony_ci	struct timer_list tl;
7362306a36Sopenharmony_ci	struct rtnl_link_stats64 stats;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	struct timer_list timer;
7662306a36Sopenharmony_ci	unsigned int timer_val;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	int index;
7962306a36Sopenharmony_ci	int mtu;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	struct {
8262306a36Sopenharmony_ci		union {
8362306a36Sopenharmony_ci			struct tuntap_info tuntap;
8462306a36Sopenharmony_ci		} info;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		const struct iss_net_ops *net_ops;
8762306a36Sopenharmony_ci	} tp;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* ================================ HELPERS ================================ */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic char *split_if_spec(char *str, ...)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	char **arg, *end;
9762306a36Sopenharmony_ci	va_list ap;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	va_start(ap, str);
10062306a36Sopenharmony_ci	while ((arg = va_arg(ap, char**)) != NULL) {
10162306a36Sopenharmony_ci		if (*str == '\0') {
10262306a36Sopenharmony_ci			va_end(ap);
10362306a36Sopenharmony_ci			return NULL;
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci		end = strchr(str, ',');
10662306a36Sopenharmony_ci		if (end != str)
10762306a36Sopenharmony_ci			*arg = str;
10862306a36Sopenharmony_ci		if (end == NULL) {
10962306a36Sopenharmony_ci			va_end(ap);
11062306a36Sopenharmony_ci			return NULL;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci		*end++ = '\0';
11362306a36Sopenharmony_ci		str = end;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci	va_end(ap);
11662306a36Sopenharmony_ci	return str;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/* Set Ethernet address of the specified device. */
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void setup_etheraddr(struct net_device *dev, char *str)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (str == NULL)
12662306a36Sopenharmony_ci		goto random;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (!mac_pton(str, addr)) {
12962306a36Sopenharmony_ci		pr_err("%s: failed to parse '%s' as an ethernet address\n",
13062306a36Sopenharmony_ci		       dev->name, str);
13162306a36Sopenharmony_ci		goto random;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci	if (is_multicast_ether_addr(addr)) {
13462306a36Sopenharmony_ci		pr_err("%s: attempt to assign a multicast ethernet address\n",
13562306a36Sopenharmony_ci		       dev->name);
13662306a36Sopenharmony_ci		goto random;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr)) {
13962306a36Sopenharmony_ci		pr_err("%s: attempt to assign an invalid ethernet address\n",
14062306a36Sopenharmony_ci		       dev->name);
14162306a36Sopenharmony_ci		goto random;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	if (!is_local_ether_addr(addr))
14462306a36Sopenharmony_ci		pr_warn("%s: assigning a globally valid ethernet address\n",
14562306a36Sopenharmony_ci			dev->name);
14662306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr);
14762306a36Sopenharmony_ci	return;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cirandom:
15062306a36Sopenharmony_ci	pr_info("%s: choosing a random ethernet address\n",
15162306a36Sopenharmony_ci		dev->name);
15262306a36Sopenharmony_ci	eth_hw_addr_random(dev);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* ======================= TUNTAP TRANSPORT INTERFACE ====================== */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int tuntap_open(struct iss_net_private *lp)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct ifreq ifr;
16062306a36Sopenharmony_ci	char *dev_name = lp->tp.info.tuntap.dev_name;
16162306a36Sopenharmony_ci	int err = -EINVAL;
16262306a36Sopenharmony_ci	int fd;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	fd = simc_open("/dev/net/tun", 02, 0); /* O_RDWR */
16562306a36Sopenharmony_ci	if (fd < 0) {
16662306a36Sopenharmony_ci		pr_err("%s: failed to open /dev/net/tun, returned %d (errno = %d)\n",
16762306a36Sopenharmony_ci		       lp->dev->name, fd, errno);
16862306a36Sopenharmony_ci		return fd;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	memset(&ifr, 0, sizeof(ifr));
17262306a36Sopenharmony_ci	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
17362306a36Sopenharmony_ci	strscpy(ifr.ifr_name, dev_name, sizeof(ifr.ifr_name));
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	err = simc_ioctl(fd, TUNSETIFF, &ifr);
17662306a36Sopenharmony_ci	if (err < 0) {
17762306a36Sopenharmony_ci		pr_err("%s: failed to set interface %s, returned %d (errno = %d)\n",
17862306a36Sopenharmony_ci		       lp->dev->name, dev_name, err, errno);
17962306a36Sopenharmony_ci		simc_close(fd);
18062306a36Sopenharmony_ci		return err;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	lp->tp.info.tuntap.fd = fd;
18462306a36Sopenharmony_ci	return err;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void tuntap_close(struct iss_net_private *lp)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	simc_close(lp->tp.info.tuntap.fd);
19062306a36Sopenharmony_ci	lp->tp.info.tuntap.fd = -1;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic int tuntap_read(struct iss_net_private *lp, struct sk_buff **skb)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	return simc_read(lp->tp.info.tuntap.fd,
19662306a36Sopenharmony_ci			(*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int tuntap_write(struct iss_net_private *lp, struct sk_buff **skb)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic unsigned short tuntap_protocol(struct sk_buff *skb)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	return eth_type_trans(skb, skb->dev);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic int tuntap_poll(struct iss_net_private *lp)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	return simc_poll(lp->tp.info.tuntap.fd);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic const struct iss_net_ops tuntap_ops = {
21562306a36Sopenharmony_ci	.open		= tuntap_open,
21662306a36Sopenharmony_ci	.close		= tuntap_close,
21762306a36Sopenharmony_ci	.read		= tuntap_read,
21862306a36Sopenharmony_ci	.write		= tuntap_write,
21962306a36Sopenharmony_ci	.protocol	= tuntap_protocol,
22062306a36Sopenharmony_ci	.poll		= tuntap_poll,
22162306a36Sopenharmony_ci};
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/*
22462306a36Sopenharmony_ci * ethX=tuntap,[mac address],device name
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic int tuntap_probe(struct iss_net_private *lp, int index, char *init)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct net_device *dev = lp->dev;
23062306a36Sopenharmony_ci	char *dev_name = NULL, *mac_str = NULL, *rem = NULL;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (strncmp(init, TRANSPORT_TUNTAP_NAME,
23562306a36Sopenharmony_ci		    sizeof(TRANSPORT_TUNTAP_NAME) - 1))
23662306a36Sopenharmony_ci		return 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	init += sizeof(TRANSPORT_TUNTAP_NAME) - 1;
23962306a36Sopenharmony_ci	if (*init == ',') {
24062306a36Sopenharmony_ci		rem = split_if_spec(init + 1, &mac_str, &dev_name, NULL);
24162306a36Sopenharmony_ci		if (rem != NULL) {
24262306a36Sopenharmony_ci			pr_err("%s: extra garbage on specification : '%s'\n",
24362306a36Sopenharmony_ci			       dev->name, rem);
24462306a36Sopenharmony_ci			return 0;
24562306a36Sopenharmony_ci		}
24662306a36Sopenharmony_ci	} else if (*init != '\0') {
24762306a36Sopenharmony_ci		pr_err("%s: invalid argument: %s. Skipping device!\n",
24862306a36Sopenharmony_ci		       dev->name, init);
24962306a36Sopenharmony_ci		return 0;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (!dev_name) {
25362306a36Sopenharmony_ci		pr_err("%s: missing tuntap device name\n", dev->name);
25462306a36Sopenharmony_ci		return 0;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	strscpy(lp->tp.info.tuntap.dev_name, dev_name,
25862306a36Sopenharmony_ci		sizeof(lp->tp.info.tuntap.dev_name));
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	setup_etheraddr(dev, mac_str);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	lp->mtu = TRANSPORT_TUNTAP_MTU;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	lp->tp.info.tuntap.fd = -1;
26562306a36Sopenharmony_ci	lp->tp.net_ops = &tuntap_ops;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return 1;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/* ================================ ISS NET ================================ */
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int iss_net_rx(struct net_device *dev)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
27562306a36Sopenharmony_ci	int pkt_len;
27662306a36Sopenharmony_ci	struct sk_buff *skb;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* Check if there is any new data. */
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (lp->tp.net_ops->poll(lp) == 0)
28162306a36Sopenharmony_ci		return 0;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	/* Try to allocate memory, if it fails, try again next round. */
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER);
28662306a36Sopenharmony_ci	if (skb == NULL) {
28762306a36Sopenharmony_ci		spin_lock_bh(&lp->lock);
28862306a36Sopenharmony_ci		lp->stats.rx_dropped++;
28962306a36Sopenharmony_ci		spin_unlock_bh(&lp->lock);
29062306a36Sopenharmony_ci		return 0;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	skb_reserve(skb, 2);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* Setup skb */
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	skb->dev = dev;
29862306a36Sopenharmony_ci	skb_reset_mac_header(skb);
29962306a36Sopenharmony_ci	pkt_len = lp->tp.net_ops->read(lp, &skb);
30062306a36Sopenharmony_ci	skb_put(skb, pkt_len);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (pkt_len > 0) {
30362306a36Sopenharmony_ci		skb_trim(skb, pkt_len);
30462306a36Sopenharmony_ci		skb->protocol = lp->tp.net_ops->protocol(skb);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		spin_lock_bh(&lp->lock);
30762306a36Sopenharmony_ci		lp->stats.rx_bytes += skb->len;
30862306a36Sopenharmony_ci		lp->stats.rx_packets++;
30962306a36Sopenharmony_ci		spin_unlock_bh(&lp->lock);
31062306a36Sopenharmony_ci		netif_rx(skb);
31162306a36Sopenharmony_ci		return pkt_len;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci	kfree_skb(skb);
31462306a36Sopenharmony_ci	return pkt_len;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int iss_net_poll(struct iss_net_private *lp)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	int err, ret = 0;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (!netif_running(lp->dev))
32262306a36Sopenharmony_ci		return 0;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	while ((err = iss_net_rx(lp->dev)) > 0)
32562306a36Sopenharmony_ci		ret++;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (err < 0) {
32862306a36Sopenharmony_ci		pr_err("Device '%s' read returned %d, shutting it down\n",
32962306a36Sopenharmony_ci		       lp->dev->name, err);
33062306a36Sopenharmony_ci		dev_close(lp->dev);
33162306a36Sopenharmony_ci	} else {
33262306a36Sopenharmony_ci		/* FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); */
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return ret;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic void iss_net_timer(struct timer_list *t)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct iss_net_private *lp = from_timer(lp, t, timer);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	iss_net_poll(lp);
34462306a36Sopenharmony_ci	mod_timer(&lp->timer, jiffies + lp->timer_val);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int iss_net_open(struct net_device *dev)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
35162306a36Sopenharmony_ci	int err;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	err = lp->tp.net_ops->open(lp);
35462306a36Sopenharmony_ci	if (err < 0)
35562306a36Sopenharmony_ci		return err;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	netif_start_queue(dev);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	/* clear buffer - it can happen that the host side of the interface
36062306a36Sopenharmony_ci	 * is full when we get here. In this case, new data is never queued,
36162306a36Sopenharmony_ci	 * SIGIOs never arrive, and the net never works.
36262306a36Sopenharmony_ci	 */
36362306a36Sopenharmony_ci	while ((err = iss_net_rx(dev)) > 0)
36462306a36Sopenharmony_ci		;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	timer_setup(&lp->timer, iss_net_timer, 0);
36762306a36Sopenharmony_ci	lp->timer_val = ISS_NET_TIMER_VALUE;
36862306a36Sopenharmony_ci	mod_timer(&lp->timer, jiffies + lp->timer_val);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	return err;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic int iss_net_close(struct net_device *dev)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	netif_stop_queue(dev);
37862306a36Sopenharmony_ci	del_timer_sync(&lp->timer);
37962306a36Sopenharmony_ci	lp->tp.net_ops->close(lp);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
38762306a36Sopenharmony_ci	int len;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	netif_stop_queue(dev);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	len = lp->tp.net_ops->write(lp, &skb);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (len == skb->len) {
39462306a36Sopenharmony_ci		spin_lock_bh(&lp->lock);
39562306a36Sopenharmony_ci		lp->stats.tx_packets++;
39662306a36Sopenharmony_ci		lp->stats.tx_bytes += skb->len;
39762306a36Sopenharmony_ci		spin_unlock_bh(&lp->lock);
39862306a36Sopenharmony_ci		netif_trans_update(dev);
39962306a36Sopenharmony_ci		netif_start_queue(dev);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		/* this is normally done in the interrupt when tx finishes */
40262306a36Sopenharmony_ci		netif_wake_queue(dev);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	} else if (len == 0) {
40562306a36Sopenharmony_ci		netif_start_queue(dev);
40662306a36Sopenharmony_ci		spin_lock_bh(&lp->lock);
40762306a36Sopenharmony_ci		lp->stats.tx_dropped++;
40862306a36Sopenharmony_ci		spin_unlock_bh(&lp->lock);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	} else {
41162306a36Sopenharmony_ci		netif_start_queue(dev);
41262306a36Sopenharmony_ci		pr_err("%s: %s failed(%d)\n", dev->name, __func__, len);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	dev_kfree_skb(skb);
41762306a36Sopenharmony_ci	return NETDEV_TX_OK;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void iss_net_get_stats64(struct net_device *dev,
42262306a36Sopenharmony_ci				struct rtnl_link_stats64 *stats)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	spin_lock_bh(&lp->lock);
42762306a36Sopenharmony_ci	*stats = lp->stats;
42862306a36Sopenharmony_ci	spin_unlock_bh(&lp->lock);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic void iss_net_set_multicast_list(struct net_device *dev)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void iss_net_tx_timeout(struct net_device *dev, unsigned int txqueue)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int iss_net_change_mtu(struct net_device *dev, int new_mtu)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	return -EINVAL;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void iss_net_user_timer_expire(struct timer_list *unused)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic struct platform_driver iss_net_driver = {
45062306a36Sopenharmony_ci	.driver = {
45162306a36Sopenharmony_ci		.name  = DRIVER_NAME,
45262306a36Sopenharmony_ci	},
45362306a36Sopenharmony_ci};
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int driver_registered;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic const struct net_device_ops iss_netdev_ops = {
45862306a36Sopenharmony_ci	.ndo_open		= iss_net_open,
45962306a36Sopenharmony_ci	.ndo_stop		= iss_net_close,
46062306a36Sopenharmony_ci	.ndo_get_stats64	= iss_net_get_stats64,
46162306a36Sopenharmony_ci	.ndo_start_xmit		= iss_net_start_xmit,
46262306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
46362306a36Sopenharmony_ci	.ndo_change_mtu		= iss_net_change_mtu,
46462306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
46562306a36Sopenharmony_ci	.ndo_tx_timeout		= iss_net_tx_timeout,
46662306a36Sopenharmony_ci	.ndo_set_rx_mode	= iss_net_set_multicast_list,
46762306a36Sopenharmony_ci};
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic void iss_net_pdev_release(struct device *dev)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
47262306a36Sopenharmony_ci	struct iss_net_private *lp =
47362306a36Sopenharmony_ci		container_of(pdev, struct iss_net_private, pdev);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	free_netdev(lp->dev);
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic void iss_net_configure(int index, char *init)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct net_device *dev;
48162306a36Sopenharmony_ci	struct iss_net_private *lp;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(*lp));
48462306a36Sopenharmony_ci	if (dev == NULL) {
48562306a36Sopenharmony_ci		pr_err("eth_configure: failed to allocate device\n");
48662306a36Sopenharmony_ci		return;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* Initialize private element. */
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	lp = netdev_priv(dev);
49262306a36Sopenharmony_ci	*lp = (struct iss_net_private) {
49362306a36Sopenharmony_ci		.dev			= dev,
49462306a36Sopenharmony_ci		.index			= index,
49562306a36Sopenharmony_ci	};
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	spin_lock_init(&lp->lock);
49862306a36Sopenharmony_ci	/*
49962306a36Sopenharmony_ci	 * If this name ends up conflicting with an existing registered
50062306a36Sopenharmony_ci	 * netdevice, that is OK, register_netdev{,ice}() will notice this
50162306a36Sopenharmony_ci	 * and fail.
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci	snprintf(dev->name, sizeof(dev->name), "eth%d", index);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/*
50662306a36Sopenharmony_ci	 * Try all transport protocols.
50762306a36Sopenharmony_ci	 * Note: more protocols can be added by adding '&& !X_init(lp, eth)'.
50862306a36Sopenharmony_ci	 */
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (!tuntap_probe(lp, index, init)) {
51162306a36Sopenharmony_ci		pr_err("%s: invalid arguments. Skipping device!\n",
51262306a36Sopenharmony_ci		       dev->name);
51362306a36Sopenharmony_ci		goto err_free_netdev;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	pr_info("Netdevice %d (%pM)\n", index, dev->dev_addr);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* sysfs register */
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (!driver_registered) {
52162306a36Sopenharmony_ci		if (platform_driver_register(&iss_net_driver))
52262306a36Sopenharmony_ci			goto err_free_netdev;
52362306a36Sopenharmony_ci		driver_registered = 1;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	lp->pdev.id = index;
52762306a36Sopenharmony_ci	lp->pdev.name = DRIVER_NAME;
52862306a36Sopenharmony_ci	lp->pdev.dev.release = iss_net_pdev_release;
52962306a36Sopenharmony_ci	if (platform_device_register(&lp->pdev))
53062306a36Sopenharmony_ci		goto err_free_netdev;
53162306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &lp->pdev.dev);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	dev->netdev_ops = &iss_netdev_ops;
53462306a36Sopenharmony_ci	dev->mtu = lp->mtu;
53562306a36Sopenharmony_ci	dev->watchdog_timeo = (HZ >> 1);
53662306a36Sopenharmony_ci	dev->irq = -1;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	rtnl_lock();
53962306a36Sopenharmony_ci	if (register_netdevice(dev)) {
54062306a36Sopenharmony_ci		rtnl_unlock();
54162306a36Sopenharmony_ci		pr_err("%s: error registering net device!\n", dev->name);
54262306a36Sopenharmony_ci		platform_device_unregister(&lp->pdev);
54362306a36Sopenharmony_ci		/* dev is freed by the iss_net_pdev_release callback */
54462306a36Sopenharmony_ci		return;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci	rtnl_unlock();
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	timer_setup(&lp->tl, iss_net_user_timer_expire, 0);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cierr_free_netdev:
55362306a36Sopenharmony_ci	free_netdev(dev);
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/* Filled in during early boot */
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistruct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistruct iss_net_init {
56362306a36Sopenharmony_ci	struct list_head list;
56462306a36Sopenharmony_ci	char *init;		/* init string */
56562306a36Sopenharmony_ci	int index;
56662306a36Sopenharmony_ci};
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/*
56962306a36Sopenharmony_ci * Parse the command line and look for 'ethX=...' fields, and register all
57062306a36Sopenharmony_ci * those fields. They will be later initialized in iss_net_init.
57162306a36Sopenharmony_ci */
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int __init iss_net_setup(char *str)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct iss_net_init *device = NULL;
57662306a36Sopenharmony_ci	struct iss_net_init *new;
57762306a36Sopenharmony_ci	struct list_head *ele;
57862306a36Sopenharmony_ci	char *end;
57962306a36Sopenharmony_ci	int rc;
58062306a36Sopenharmony_ci	unsigned n;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	end = strchr(str, '=');
58362306a36Sopenharmony_ci	if (!end) {
58462306a36Sopenharmony_ci		pr_err("Expected '=' after device number\n");
58562306a36Sopenharmony_ci		return 1;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci	*end = 0;
58862306a36Sopenharmony_ci	rc = kstrtouint(str, 0, &n);
58962306a36Sopenharmony_ci	*end = '=';
59062306a36Sopenharmony_ci	if (rc < 0) {
59162306a36Sopenharmony_ci		pr_err("Failed to parse '%s'\n", str);
59262306a36Sopenharmony_ci		return 1;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci	str = end;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	list_for_each(ele, &eth_cmd_line) {
59762306a36Sopenharmony_ci		device = list_entry(ele, struct iss_net_init, list);
59862306a36Sopenharmony_ci		if (device->index == n)
59962306a36Sopenharmony_ci			break;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (device && device->index == n) {
60362306a36Sopenharmony_ci		pr_err("Device %u already configured\n", n);
60462306a36Sopenharmony_ci		return 1;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	new = memblock_alloc(sizeof(*new), SMP_CACHE_BYTES);
60862306a36Sopenharmony_ci	if (new == NULL) {
60962306a36Sopenharmony_ci		pr_err("Alloc_bootmem failed\n");
61062306a36Sopenharmony_ci		return 1;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	INIT_LIST_HEAD(&new->list);
61462306a36Sopenharmony_ci	new->index = n;
61562306a36Sopenharmony_ci	new->init = str + 1;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	list_add_tail(&new->list, &eth_cmd_line);
61862306a36Sopenharmony_ci	return 1;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci__setup("eth", iss_net_setup);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci/*
62462306a36Sopenharmony_ci * Initialize all ISS Ethernet devices previously registered in iss_net_setup.
62562306a36Sopenharmony_ci */
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic int iss_net_init(void)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct list_head *ele, *next;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* Walk through all Ethernet devices specified in the command line. */
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	list_for_each_safe(ele, next, &eth_cmd_line) {
63462306a36Sopenharmony_ci		struct iss_net_init *eth;
63562306a36Sopenharmony_ci		eth = list_entry(ele, struct iss_net_init, list);
63662306a36Sopenharmony_ci		iss_net_configure(eth->index, eth->init);
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return 1;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_cidevice_initcall(iss_net_init);
642