18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * arch/xtensa/platforms/iss/network.c
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Platform specific initialization.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Authors: Chris Zankel <chris@zankel.net>
98c2ecf20Sopenharmony_ci * Based on work form the UML team.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright 2005 Tensilica Inc.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/list.h>
178c2ecf20Sopenharmony_ci#include <linux/irq.h>
188c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/timer.h>
218c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
228c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci#include <linux/if_tun.h>
258c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
268c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
278c2ecf20Sopenharmony_ci#include <linux/ioctl.h>
288c2ecf20Sopenharmony_ci#include <linux/memblock.h>
298c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
308c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
318c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <platform/simcall.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define DRIVER_NAME "iss-netdev"
368c2ecf20Sopenharmony_ci#define ETH_MAX_PACKET 1500
378c2ecf20Sopenharmony_ci#define ETH_HEADER_OTHER 14
388c2ecf20Sopenharmony_ci#define ISS_NET_TIMER_VALUE (HZ / 10)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(opened_lock);
428c2ecf20Sopenharmony_cistatic LIST_HEAD(opened);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(devices_lock);
458c2ecf20Sopenharmony_cistatic LIST_HEAD(devices);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* We currently only support the TUNTAP transport protocol. */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define TRANSPORT_TUNTAP_NAME "tuntap"
528c2ecf20Sopenharmony_ci#define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct tuntap_info {
558c2ecf20Sopenharmony_ci	char dev_name[IFNAMSIZ];
568c2ecf20Sopenharmony_ci	int fd;
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* This structure contains out private information for the driver. */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistruct iss_net_private {
658c2ecf20Sopenharmony_ci	struct list_head device_list;
668c2ecf20Sopenharmony_ci	struct list_head opened_list;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	spinlock_t lock;
698c2ecf20Sopenharmony_ci	struct net_device *dev;
708c2ecf20Sopenharmony_ci	struct platform_device pdev;
718c2ecf20Sopenharmony_ci	struct timer_list tl;
728c2ecf20Sopenharmony_ci	struct net_device_stats stats;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	struct timer_list timer;
758c2ecf20Sopenharmony_ci	unsigned int timer_val;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	int index;
788c2ecf20Sopenharmony_ci	int mtu;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	struct {
818c2ecf20Sopenharmony_ci		union {
828c2ecf20Sopenharmony_ci			struct tuntap_info tuntap;
838c2ecf20Sopenharmony_ci		} info;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		int (*open)(struct iss_net_private *lp);
868c2ecf20Sopenharmony_ci		void (*close)(struct iss_net_private *lp);
878c2ecf20Sopenharmony_ci		int (*read)(struct iss_net_private *lp, struct sk_buff **skb);
888c2ecf20Sopenharmony_ci		int (*write)(struct iss_net_private *lp, struct sk_buff **skb);
898c2ecf20Sopenharmony_ci		unsigned short (*protocol)(struct sk_buff *skb);
908c2ecf20Sopenharmony_ci		int (*poll)(struct iss_net_private *lp);
918c2ecf20Sopenharmony_ci	} tp;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* ================================ HELPERS ================================ */
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic char *split_if_spec(char *str, ...)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	char **arg, *end;
1018c2ecf20Sopenharmony_ci	va_list ap;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	va_start(ap, str);
1048c2ecf20Sopenharmony_ci	while ((arg = va_arg(ap, char**)) != NULL) {
1058c2ecf20Sopenharmony_ci		if (*str == '\0') {
1068c2ecf20Sopenharmony_ci			va_end(ap);
1078c2ecf20Sopenharmony_ci			return NULL;
1088c2ecf20Sopenharmony_ci		}
1098c2ecf20Sopenharmony_ci		end = strchr(str, ',');
1108c2ecf20Sopenharmony_ci		if (end != str)
1118c2ecf20Sopenharmony_ci			*arg = str;
1128c2ecf20Sopenharmony_ci		if (end == NULL) {
1138c2ecf20Sopenharmony_ci			va_end(ap);
1148c2ecf20Sopenharmony_ci			return NULL;
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci		*end++ = '\0';
1178c2ecf20Sopenharmony_ci		str = end;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci	va_end(ap);
1208c2ecf20Sopenharmony_ci	return str;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/* Set Ethernet address of the specified device. */
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void setup_etheraddr(struct net_device *dev, char *str)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	unsigned char *addr = dev->dev_addr;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (str == NULL)
1308c2ecf20Sopenharmony_ci		goto random;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (!mac_pton(str, addr)) {
1338c2ecf20Sopenharmony_ci		pr_err("%s: failed to parse '%s' as an ethernet address\n",
1348c2ecf20Sopenharmony_ci		       dev->name, str);
1358c2ecf20Sopenharmony_ci		goto random;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci	if (is_multicast_ether_addr(addr)) {
1388c2ecf20Sopenharmony_ci		pr_err("%s: attempt to assign a multicast ethernet address\n",
1398c2ecf20Sopenharmony_ci		       dev->name);
1408c2ecf20Sopenharmony_ci		goto random;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(addr)) {
1438c2ecf20Sopenharmony_ci		pr_err("%s: attempt to assign an invalid ethernet address\n",
1448c2ecf20Sopenharmony_ci		       dev->name);
1458c2ecf20Sopenharmony_ci		goto random;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci	if (!is_local_ether_addr(addr))
1488c2ecf20Sopenharmony_ci		pr_warn("%s: assigning a globally valid ethernet address\n",
1498c2ecf20Sopenharmony_ci			dev->name);
1508c2ecf20Sopenharmony_ci	return;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cirandom:
1538c2ecf20Sopenharmony_ci	pr_info("%s: choosing a random ethernet address\n",
1548c2ecf20Sopenharmony_ci		dev->name);
1558c2ecf20Sopenharmony_ci	eth_hw_addr_random(dev);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/* ======================= TUNTAP TRANSPORT INTERFACE ====================== */
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int tuntap_open(struct iss_net_private *lp)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct ifreq ifr;
1638c2ecf20Sopenharmony_ci	char *dev_name = lp->tp.info.tuntap.dev_name;
1648c2ecf20Sopenharmony_ci	int err = -EINVAL;
1658c2ecf20Sopenharmony_ci	int fd;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	fd = simc_open("/dev/net/tun", 02, 0); /* O_RDWR */
1688c2ecf20Sopenharmony_ci	if (fd < 0) {
1698c2ecf20Sopenharmony_ci		pr_err("%s: failed to open /dev/net/tun, returned %d (errno = %d)\n",
1708c2ecf20Sopenharmony_ci		       lp->dev->name, fd, errno);
1718c2ecf20Sopenharmony_ci		return fd;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	memset(&ifr, 0, sizeof(ifr));
1758c2ecf20Sopenharmony_ci	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
1768c2ecf20Sopenharmony_ci	strlcpy(ifr.ifr_name, dev_name, sizeof(ifr.ifr_name));
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	err = simc_ioctl(fd, TUNSETIFF, &ifr);
1798c2ecf20Sopenharmony_ci	if (err < 0) {
1808c2ecf20Sopenharmony_ci		pr_err("%s: failed to set interface %s, returned %d (errno = %d)\n",
1818c2ecf20Sopenharmony_ci		       lp->dev->name, dev_name, err, errno);
1828c2ecf20Sopenharmony_ci		simc_close(fd);
1838c2ecf20Sopenharmony_ci		return err;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	lp->tp.info.tuntap.fd = fd;
1878c2ecf20Sopenharmony_ci	return err;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic void tuntap_close(struct iss_net_private *lp)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	simc_close(lp->tp.info.tuntap.fd);
1938c2ecf20Sopenharmony_ci	lp->tp.info.tuntap.fd = -1;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int tuntap_read(struct iss_net_private *lp, struct sk_buff **skb)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	return simc_read(lp->tp.info.tuntap.fd,
1998c2ecf20Sopenharmony_ci			(*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int tuntap_write(struct iss_net_private *lp, struct sk_buff **skb)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic unsigned short tuntap_protocol(struct sk_buff *skb)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	return eth_type_trans(skb, skb->dev);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int tuntap_poll(struct iss_net_private *lp)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	return simc_poll(lp->tp.info.tuntap.fd);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/*
2188c2ecf20Sopenharmony_ci * ethX=tuntap,[mac address],device name
2198c2ecf20Sopenharmony_ci */
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic int tuntap_probe(struct iss_net_private *lp, int index, char *init)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct net_device *dev = lp->dev;
2248c2ecf20Sopenharmony_ci	char *dev_name = NULL, *mac_str = NULL, *rem = NULL;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (strncmp(init, TRANSPORT_TUNTAP_NAME,
2298c2ecf20Sopenharmony_ci		    sizeof(TRANSPORT_TUNTAP_NAME) - 1))
2308c2ecf20Sopenharmony_ci		return 0;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	init += sizeof(TRANSPORT_TUNTAP_NAME) - 1;
2338c2ecf20Sopenharmony_ci	if (*init == ',') {
2348c2ecf20Sopenharmony_ci		rem = split_if_spec(init + 1, &mac_str, &dev_name, NULL);
2358c2ecf20Sopenharmony_ci		if (rem != NULL) {
2368c2ecf20Sopenharmony_ci			pr_err("%s: extra garbage on specification : '%s'\n",
2378c2ecf20Sopenharmony_ci			       dev->name, rem);
2388c2ecf20Sopenharmony_ci			return 0;
2398c2ecf20Sopenharmony_ci		}
2408c2ecf20Sopenharmony_ci	} else if (*init != '\0') {
2418c2ecf20Sopenharmony_ci		pr_err("%s: invalid argument: %s. Skipping device!\n",
2428c2ecf20Sopenharmony_ci		       dev->name, init);
2438c2ecf20Sopenharmony_ci		return 0;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (!dev_name) {
2478c2ecf20Sopenharmony_ci		pr_err("%s: missing tuntap device name\n", dev->name);
2488c2ecf20Sopenharmony_ci		return 0;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	strlcpy(lp->tp.info.tuntap.dev_name, dev_name,
2528c2ecf20Sopenharmony_ci		sizeof(lp->tp.info.tuntap.dev_name));
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	setup_etheraddr(dev, mac_str);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	lp->mtu = TRANSPORT_TUNTAP_MTU;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	lp->tp.info.tuntap.fd = -1;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	lp->tp.open = tuntap_open;
2618c2ecf20Sopenharmony_ci	lp->tp.close = tuntap_close;
2628c2ecf20Sopenharmony_ci	lp->tp.read = tuntap_read;
2638c2ecf20Sopenharmony_ci	lp->tp.write = tuntap_write;
2648c2ecf20Sopenharmony_ci	lp->tp.protocol = tuntap_protocol;
2658c2ecf20Sopenharmony_ci	lp->tp.poll = tuntap_poll;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return 1;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/* ================================ ISS NET ================================ */
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic int iss_net_rx(struct net_device *dev)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
2758c2ecf20Sopenharmony_ci	int pkt_len;
2768c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* Check if there is any new data. */
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (lp->tp.poll(lp) == 0)
2818c2ecf20Sopenharmony_ci		return 0;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* Try to allocate memory, if it fails, try again next round. */
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER);
2868c2ecf20Sopenharmony_ci	if (skb == NULL) {
2878c2ecf20Sopenharmony_ci		lp->stats.rx_dropped++;
2888c2ecf20Sopenharmony_ci		return 0;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	skb_reserve(skb, 2);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* Setup skb */
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	skb->dev = dev;
2968c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
2978c2ecf20Sopenharmony_ci	pkt_len = lp->tp.read(lp, &skb);
2988c2ecf20Sopenharmony_ci	skb_put(skb, pkt_len);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (pkt_len > 0) {
3018c2ecf20Sopenharmony_ci		skb_trim(skb, pkt_len);
3028c2ecf20Sopenharmony_ci		skb->protocol = lp->tp.protocol(skb);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		lp->stats.rx_bytes += skb->len;
3058c2ecf20Sopenharmony_ci		lp->stats.rx_packets++;
3068c2ecf20Sopenharmony_ci		netif_rx_ni(skb);
3078c2ecf20Sopenharmony_ci		return pkt_len;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci	kfree_skb(skb);
3108c2ecf20Sopenharmony_ci	return pkt_len;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int iss_net_poll(void)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct list_head *ele;
3168c2ecf20Sopenharmony_ci	int err, ret = 0;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	spin_lock(&opened_lock);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	list_for_each(ele, &opened) {
3218c2ecf20Sopenharmony_ci		struct iss_net_private *lp;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		lp = list_entry(ele, struct iss_net_private, opened_list);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		if (!netif_running(lp->dev))
3268c2ecf20Sopenharmony_ci			break;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		spin_lock(&lp->lock);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		while ((err = iss_net_rx(lp->dev)) > 0)
3318c2ecf20Sopenharmony_ci			ret++;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		spin_unlock(&lp->lock);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		if (err < 0) {
3368c2ecf20Sopenharmony_ci			pr_err("Device '%s' read returned %d, shutting it down\n",
3378c2ecf20Sopenharmony_ci			       lp->dev->name, err);
3388c2ecf20Sopenharmony_ci			dev_close(lp->dev);
3398c2ecf20Sopenharmony_ci		} else {
3408c2ecf20Sopenharmony_ci			/* FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); */
3418c2ecf20Sopenharmony_ci		}
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	spin_unlock(&opened_lock);
3458c2ecf20Sopenharmony_ci	return ret;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic void iss_net_timer(struct timer_list *t)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct iss_net_private *lp = from_timer(lp, t, timer);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	iss_net_poll();
3548c2ecf20Sopenharmony_ci	spin_lock(&lp->lock);
3558c2ecf20Sopenharmony_ci	mod_timer(&lp->timer, jiffies + lp->timer_val);
3568c2ecf20Sopenharmony_ci	spin_unlock(&lp->lock);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int iss_net_open(struct net_device *dev)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
3638c2ecf20Sopenharmony_ci	int err;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	spin_lock_bh(&lp->lock);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	err = lp->tp.open(lp);
3688c2ecf20Sopenharmony_ci	if (err < 0)
3698c2ecf20Sopenharmony_ci		goto out;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	netif_start_queue(dev);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/* clear buffer - it can happen that the host side of the interface
3748c2ecf20Sopenharmony_ci	 * is full when we get here. In this case, new data is never queued,
3758c2ecf20Sopenharmony_ci	 * SIGIOs never arrive, and the net never works.
3768c2ecf20Sopenharmony_ci	 */
3778c2ecf20Sopenharmony_ci	while ((err = iss_net_rx(dev)) > 0)
3788c2ecf20Sopenharmony_ci		;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	spin_unlock_bh(&lp->lock);
3818c2ecf20Sopenharmony_ci	spin_lock_bh(&opened_lock);
3828c2ecf20Sopenharmony_ci	list_add(&lp->opened_list, &opened);
3838c2ecf20Sopenharmony_ci	spin_unlock_bh(&opened_lock);
3848c2ecf20Sopenharmony_ci	spin_lock_bh(&lp->lock);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	timer_setup(&lp->timer, iss_net_timer, 0);
3878c2ecf20Sopenharmony_ci	lp->timer_val = ISS_NET_TIMER_VALUE;
3888c2ecf20Sopenharmony_ci	mod_timer(&lp->timer, jiffies + lp->timer_val);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ciout:
3918c2ecf20Sopenharmony_ci	spin_unlock_bh(&lp->lock);
3928c2ecf20Sopenharmony_ci	return err;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int iss_net_close(struct net_device *dev)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
3988c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
3998c2ecf20Sopenharmony_ci	spin_lock_bh(&lp->lock);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	spin_lock(&opened_lock);
4028c2ecf20Sopenharmony_ci	list_del(&opened);
4038c2ecf20Sopenharmony_ci	spin_unlock(&opened_lock);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	del_timer_sync(&lp->timer);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	lp->tp.close(lp);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	spin_unlock_bh(&lp->lock);
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
4168c2ecf20Sopenharmony_ci	int len;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
4198c2ecf20Sopenharmony_ci	spin_lock_bh(&lp->lock);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	len = lp->tp.write(lp, &skb);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (len == skb->len) {
4248c2ecf20Sopenharmony_ci		lp->stats.tx_packets++;
4258c2ecf20Sopenharmony_ci		lp->stats.tx_bytes += skb->len;
4268c2ecf20Sopenharmony_ci		netif_trans_update(dev);
4278c2ecf20Sopenharmony_ci		netif_start_queue(dev);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		/* this is normally done in the interrupt when tx finishes */
4308c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	} else if (len == 0) {
4338c2ecf20Sopenharmony_ci		netif_start_queue(dev);
4348c2ecf20Sopenharmony_ci		lp->stats.tx_dropped++;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	} else {
4378c2ecf20Sopenharmony_ci		netif_start_queue(dev);
4388c2ecf20Sopenharmony_ci		pr_err("%s: %s failed(%d)\n", dev->name, __func__, len);
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	spin_unlock_bh(&lp->lock);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
4448c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic struct net_device_stats *iss_net_get_stats(struct net_device *dev)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
4518c2ecf20Sopenharmony_ci	return &lp->stats;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic void iss_net_set_multicast_list(struct net_device *dev)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic void iss_net_tx_timeout(struct net_device *dev, unsigned int txqueue)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int iss_net_set_mac(struct net_device *dev, void *addr)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct iss_net_private *lp = netdev_priv(dev);
4658c2ecf20Sopenharmony_ci	struct sockaddr *hwaddr = addr;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(hwaddr->sa_data))
4688c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
4698c2ecf20Sopenharmony_ci	spin_lock_bh(&lp->lock);
4708c2ecf20Sopenharmony_ci	memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);
4718c2ecf20Sopenharmony_ci	spin_unlock_bh(&lp->lock);
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int iss_net_change_mtu(struct net_device *dev, int new_mtu)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	return -EINVAL;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic void iss_net_user_timer_expire(struct timer_list *unused)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic struct platform_driver iss_net_driver = {
4868c2ecf20Sopenharmony_ci	.driver = {
4878c2ecf20Sopenharmony_ci		.name  = DRIVER_NAME,
4888c2ecf20Sopenharmony_ci	},
4898c2ecf20Sopenharmony_ci};
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic int driver_registered;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic const struct net_device_ops iss_netdev_ops = {
4948c2ecf20Sopenharmony_ci	.ndo_open		= iss_net_open,
4958c2ecf20Sopenharmony_ci	.ndo_stop		= iss_net_close,
4968c2ecf20Sopenharmony_ci	.ndo_get_stats		= iss_net_get_stats,
4978c2ecf20Sopenharmony_ci	.ndo_start_xmit		= iss_net_start_xmit,
4988c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
4998c2ecf20Sopenharmony_ci	.ndo_change_mtu		= iss_net_change_mtu,
5008c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= iss_net_set_mac,
5018c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= iss_net_tx_timeout,
5028c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= iss_net_set_multicast_list,
5038c2ecf20Sopenharmony_ci};
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic void iss_net_pdev_release(struct device *dev)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
5088c2ecf20Sopenharmony_ci	struct iss_net_private *lp =
5098c2ecf20Sopenharmony_ci		container_of(pdev, struct iss_net_private, pdev);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	free_netdev(lp->dev);
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic void iss_net_configure(int index, char *init)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	struct net_device *dev;
5178c2ecf20Sopenharmony_ci	struct iss_net_private *lp;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(*lp));
5208c2ecf20Sopenharmony_ci	if (dev == NULL) {
5218c2ecf20Sopenharmony_ci		pr_err("eth_configure: failed to allocate device\n");
5228c2ecf20Sopenharmony_ci		return;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	/* Initialize private element. */
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	lp = netdev_priv(dev);
5288c2ecf20Sopenharmony_ci	*lp = (struct iss_net_private) {
5298c2ecf20Sopenharmony_ci		.device_list		= LIST_HEAD_INIT(lp->device_list),
5308c2ecf20Sopenharmony_ci		.opened_list		= LIST_HEAD_INIT(lp->opened_list),
5318c2ecf20Sopenharmony_ci		.dev			= dev,
5328c2ecf20Sopenharmony_ci		.index			= index,
5338c2ecf20Sopenharmony_ci	};
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	spin_lock_init(&lp->lock);
5368c2ecf20Sopenharmony_ci	/*
5378c2ecf20Sopenharmony_ci	 * If this name ends up conflicting with an existing registered
5388c2ecf20Sopenharmony_ci	 * netdevice, that is OK, register_netdev{,ice}() will notice this
5398c2ecf20Sopenharmony_ci	 * and fail.
5408c2ecf20Sopenharmony_ci	 */
5418c2ecf20Sopenharmony_ci	snprintf(dev->name, sizeof(dev->name), "eth%d", index);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/*
5448c2ecf20Sopenharmony_ci	 * Try all transport protocols.
5458c2ecf20Sopenharmony_ci	 * Note: more protocols can be added by adding '&& !X_init(lp, eth)'.
5468c2ecf20Sopenharmony_ci	 */
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	if (!tuntap_probe(lp, index, init)) {
5498c2ecf20Sopenharmony_ci		pr_err("%s: invalid arguments. Skipping device!\n",
5508c2ecf20Sopenharmony_ci		       dev->name);
5518c2ecf20Sopenharmony_ci		goto err_free_netdev;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	pr_info("Netdevice %d (%pM)\n", index, dev->dev_addr);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	/* sysfs register */
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (!driver_registered) {
5598c2ecf20Sopenharmony_ci		if (platform_driver_register(&iss_net_driver))
5608c2ecf20Sopenharmony_ci			goto err_free_netdev;
5618c2ecf20Sopenharmony_ci		driver_registered = 1;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	spin_lock(&devices_lock);
5658c2ecf20Sopenharmony_ci	list_add(&lp->device_list, &devices);
5668c2ecf20Sopenharmony_ci	spin_unlock(&devices_lock);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	lp->pdev.id = index;
5698c2ecf20Sopenharmony_ci	lp->pdev.name = DRIVER_NAME;
5708c2ecf20Sopenharmony_ci	lp->pdev.dev.release = iss_net_pdev_release;
5718c2ecf20Sopenharmony_ci	if (platform_device_register(&lp->pdev))
5728c2ecf20Sopenharmony_ci		goto err_free_netdev;
5738c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &lp->pdev.dev);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	dev->netdev_ops = &iss_netdev_ops;
5768c2ecf20Sopenharmony_ci	dev->mtu = lp->mtu;
5778c2ecf20Sopenharmony_ci	dev->watchdog_timeo = (HZ >> 1);
5788c2ecf20Sopenharmony_ci	dev->irq = -1;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	rtnl_lock();
5818c2ecf20Sopenharmony_ci	if (register_netdevice(dev)) {
5828c2ecf20Sopenharmony_ci		rtnl_unlock();
5838c2ecf20Sopenharmony_ci		pr_err("%s: error registering net device!\n", dev->name);
5848c2ecf20Sopenharmony_ci		platform_device_unregister(&lp->pdev);
5858c2ecf20Sopenharmony_ci		return;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci	rtnl_unlock();
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	timer_setup(&lp->tl, iss_net_user_timer_expire, 0);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cierr_free_netdev:
5948c2ecf20Sopenharmony_ci	free_netdev(dev);
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci/* Filled in during early boot */
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistruct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistruct iss_net_init {
6048c2ecf20Sopenharmony_ci	struct list_head list;
6058c2ecf20Sopenharmony_ci	char *init;		/* init string */
6068c2ecf20Sopenharmony_ci	int index;
6078c2ecf20Sopenharmony_ci};
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci/*
6108c2ecf20Sopenharmony_ci * Parse the command line and look for 'ethX=...' fields, and register all
6118c2ecf20Sopenharmony_ci * those fields. They will be later initialized in iss_net_init.
6128c2ecf20Sopenharmony_ci */
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic int __init iss_net_setup(char *str)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	struct iss_net_private *device = NULL;
6178c2ecf20Sopenharmony_ci	struct iss_net_init *new;
6188c2ecf20Sopenharmony_ci	struct list_head *ele;
6198c2ecf20Sopenharmony_ci	char *end;
6208c2ecf20Sopenharmony_ci	int rc;
6218c2ecf20Sopenharmony_ci	unsigned n;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	end = strchr(str, '=');
6248c2ecf20Sopenharmony_ci	if (!end) {
6258c2ecf20Sopenharmony_ci		pr_err("Expected '=' after device number\n");
6268c2ecf20Sopenharmony_ci		return 1;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci	*end = 0;
6298c2ecf20Sopenharmony_ci	rc = kstrtouint(str, 0, &n);
6308c2ecf20Sopenharmony_ci	*end = '=';
6318c2ecf20Sopenharmony_ci	if (rc < 0) {
6328c2ecf20Sopenharmony_ci		pr_err("Failed to parse '%s'\n", str);
6338c2ecf20Sopenharmony_ci		return 1;
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci	str = end;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	spin_lock(&devices_lock);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	list_for_each(ele, &devices) {
6408c2ecf20Sopenharmony_ci		device = list_entry(ele, struct iss_net_private, device_list);
6418c2ecf20Sopenharmony_ci		if (device->index == n)
6428c2ecf20Sopenharmony_ci			break;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	spin_unlock(&devices_lock);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (device && device->index == n) {
6488c2ecf20Sopenharmony_ci		pr_err("Device %u already configured\n", n);
6498c2ecf20Sopenharmony_ci		return 1;
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	new = memblock_alloc(sizeof(*new), SMP_CACHE_BYTES);
6538c2ecf20Sopenharmony_ci	if (new == NULL) {
6548c2ecf20Sopenharmony_ci		pr_err("Alloc_bootmem failed\n");
6558c2ecf20Sopenharmony_ci		return 1;
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&new->list);
6598c2ecf20Sopenharmony_ci	new->index = n;
6608c2ecf20Sopenharmony_ci	new->init = str + 1;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	list_add_tail(&new->list, &eth_cmd_line);
6638c2ecf20Sopenharmony_ci	return 1;
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci__setup("eth", iss_net_setup);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci/*
6698c2ecf20Sopenharmony_ci * Initialize all ISS Ethernet devices previously registered in iss_net_setup.
6708c2ecf20Sopenharmony_ci */
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic int iss_net_init(void)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	struct list_head *ele, *next;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* Walk through all Ethernet devices specified in the command line. */
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	list_for_each_safe(ele, next, &eth_cmd_line) {
6798c2ecf20Sopenharmony_ci		struct iss_net_init *eth;
6808c2ecf20Sopenharmony_ci		eth = list_entry(ele, struct iss_net_init, list);
6818c2ecf20Sopenharmony_ci		iss_net_configure(eth->index, eth->init);
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	return 1;
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_cidevice_initcall(iss_net_init);
687