18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
48c2ecf20Sopenharmony_ci * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
58c2ecf20Sopenharmony_ci * James Leu (jleu@mindspring.net).
68c2ecf20Sopenharmony_ci * Copyright (C) 2001 by various other people who didn't put their name here.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/memblock.h>
108c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
118c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
128c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/list.h>
158c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
188c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
218c2ecf20Sopenharmony_ci#include <init.h>
228c2ecf20Sopenharmony_ci#include <irq_kern.h>
238c2ecf20Sopenharmony_ci#include <irq_user.h>
248c2ecf20Sopenharmony_ci#include "mconsole_kern.h"
258c2ecf20Sopenharmony_ci#include <net_kern.h>
268c2ecf20Sopenharmony_ci#include <net_user.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define DRIVER_NAME "uml-netdev"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(opened_lock);
318c2ecf20Sopenharmony_cistatic LIST_HEAD(opened);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * The drop_skb is used when we can't allocate an skb.  The
358c2ecf20Sopenharmony_ci * packet is read into drop_skb in order to get the data off the
368c2ecf20Sopenharmony_ci * connection to the host.
378c2ecf20Sopenharmony_ci * It is reallocated whenever a maximum packet size is seen which is
388c2ecf20Sopenharmony_ci * larger than any seen before.  update_drop_skb is called from
398c2ecf20Sopenharmony_ci * eth_configure when a new interface is added.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(drop_lock);
428c2ecf20Sopenharmony_cistatic struct sk_buff *drop_skb;
438c2ecf20Sopenharmony_cistatic int drop_max;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int update_drop_skb(int max)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct sk_buff *new;
488c2ecf20Sopenharmony_ci	unsigned long flags;
498c2ecf20Sopenharmony_ci	int err = 0;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	spin_lock_irqsave(&drop_lock, flags);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (max <= drop_max)
548c2ecf20Sopenharmony_ci		goto out;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	err = -ENOMEM;
578c2ecf20Sopenharmony_ci	new = dev_alloc_skb(max);
588c2ecf20Sopenharmony_ci	if (new == NULL)
598c2ecf20Sopenharmony_ci		goto out;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	skb_put(new, max);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	kfree_skb(drop_skb);
648c2ecf20Sopenharmony_ci	drop_skb = new;
658c2ecf20Sopenharmony_ci	drop_max = max;
668c2ecf20Sopenharmony_ci	err = 0;
678c2ecf20Sopenharmony_ciout:
688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&drop_lock, flags);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return err;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int uml_net_rx(struct net_device *dev)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct uml_net_private *lp = netdev_priv(dev);
768c2ecf20Sopenharmony_ci	int pkt_len;
778c2ecf20Sopenharmony_ci	struct sk_buff *skb;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* If we can't allocate memory, try again next round. */
808c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(lp->max_packet);
818c2ecf20Sopenharmony_ci	if (skb == NULL) {
828c2ecf20Sopenharmony_ci		drop_skb->dev = dev;
838c2ecf20Sopenharmony_ci		/* Read a packet into drop_skb and don't do anything with it. */
848c2ecf20Sopenharmony_ci		(*lp->read)(lp->fd, drop_skb, lp);
858c2ecf20Sopenharmony_ci		dev->stats.rx_dropped++;
868c2ecf20Sopenharmony_ci		return 0;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	skb->dev = dev;
908c2ecf20Sopenharmony_ci	skb_put(skb, lp->max_packet);
918c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
928c2ecf20Sopenharmony_ci	pkt_len = (*lp->read)(lp->fd, skb, lp);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (pkt_len > 0) {
958c2ecf20Sopenharmony_ci		skb_trim(skb, pkt_len);
968c2ecf20Sopenharmony_ci		skb->protocol = (*lp->protocol)(skb);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		dev->stats.rx_bytes += skb->len;
998c2ecf20Sopenharmony_ci		dev->stats.rx_packets++;
1008c2ecf20Sopenharmony_ci		netif_rx(skb);
1018c2ecf20Sopenharmony_ci		return pkt_len;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	kfree_skb(skb);
1058c2ecf20Sopenharmony_ci	return pkt_len;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void uml_dev_close(struct work_struct *work)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct uml_net_private *lp =
1118c2ecf20Sopenharmony_ci		container_of(work, struct uml_net_private, work);
1128c2ecf20Sopenharmony_ci	dev_close(lp->dev);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic irqreturn_t uml_net_interrupt(int irq, void *dev_id)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
1188c2ecf20Sopenharmony_ci	struct uml_net_private *lp = netdev_priv(dev);
1198c2ecf20Sopenharmony_ci	int err;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (!netif_running(dev))
1228c2ecf20Sopenharmony_ci		return IRQ_NONE;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	spin_lock(&lp->lock);
1258c2ecf20Sopenharmony_ci	while ((err = uml_net_rx(dev)) > 0) ;
1268c2ecf20Sopenharmony_ci	if (err < 0) {
1278c2ecf20Sopenharmony_ci		printk(KERN_ERR
1288c2ecf20Sopenharmony_ci		       "Device '%s' read returned %d, shutting it down\n",
1298c2ecf20Sopenharmony_ci		       dev->name, err);
1308c2ecf20Sopenharmony_ci		/* dev_close can't be called in interrupt context, and takes
1318c2ecf20Sopenharmony_ci		 * again lp->lock.
1328c2ecf20Sopenharmony_ci		 * And dev_close() can be safely called multiple times on the
1338c2ecf20Sopenharmony_ci		 * same device, since it tests for (dev->flags & IFF_UP). So
1348c2ecf20Sopenharmony_ci		 * there's no harm in delaying the device shutdown.
1358c2ecf20Sopenharmony_ci		 * Furthermore, the workqueue will not re-enqueue an already
1368c2ecf20Sopenharmony_ci		 * enqueued work item. */
1378c2ecf20Sopenharmony_ci		schedule_work(&lp->work);
1388c2ecf20Sopenharmony_ci		goto out;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ciout:
1418c2ecf20Sopenharmony_ci	spin_unlock(&lp->lock);
1428c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int uml_net_open(struct net_device *dev)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct uml_net_private *lp = netdev_priv(dev);
1488c2ecf20Sopenharmony_ci	int err;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (lp->fd >= 0) {
1518c2ecf20Sopenharmony_ci		err = -ENXIO;
1528c2ecf20Sopenharmony_ci		goto out;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	lp->fd = (*lp->open)(&lp->user);
1568c2ecf20Sopenharmony_ci	if (lp->fd < 0) {
1578c2ecf20Sopenharmony_ci		err = lp->fd;
1588c2ecf20Sopenharmony_ci		goto out;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,
1628c2ecf20Sopenharmony_ci			     IRQF_SHARED, dev->name, dev);
1638c2ecf20Sopenharmony_ci	if (err != 0) {
1648c2ecf20Sopenharmony_ci		printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
1658c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
1668c2ecf20Sopenharmony_ci		goto out_close;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	netif_start_queue(dev);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* clear buffer - it can happen that the host side of the interface
1728c2ecf20Sopenharmony_ci	 * is full when we get here.  In this case, new data is never queued,
1738c2ecf20Sopenharmony_ci	 * SIGIOs never arrive, and the net never works.
1748c2ecf20Sopenharmony_ci	 */
1758c2ecf20Sopenharmony_ci	while ((err = uml_net_rx(dev)) > 0) ;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	spin_lock(&opened_lock);
1788c2ecf20Sopenharmony_ci	list_add(&lp->list, &opened);
1798c2ecf20Sopenharmony_ci	spin_unlock(&opened_lock);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return 0;
1828c2ecf20Sopenharmony_ciout_close:
1838c2ecf20Sopenharmony_ci	if (lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
1848c2ecf20Sopenharmony_ci	lp->fd = -1;
1858c2ecf20Sopenharmony_ciout:
1868c2ecf20Sopenharmony_ci	return err;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int uml_net_close(struct net_device *dev)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct uml_net_private *lp = netdev_priv(dev);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	um_free_irq(dev->irq, dev);
1968c2ecf20Sopenharmony_ci	if (lp->close != NULL)
1978c2ecf20Sopenharmony_ci		(*lp->close)(lp->fd, &lp->user);
1988c2ecf20Sopenharmony_ci	lp->fd = -1;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	spin_lock(&opened_lock);
2018c2ecf20Sopenharmony_ci	list_del(&lp->list);
2028c2ecf20Sopenharmony_ci	spin_unlock(&opened_lock);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return 0;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic netdev_tx_t uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct uml_net_private *lp = netdev_priv(dev);
2108c2ecf20Sopenharmony_ci	unsigned long flags;
2118c2ecf20Sopenharmony_ci	int len;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	len = (*lp->write)(lp->fd, skb, lp);
2188c2ecf20Sopenharmony_ci	skb_tx_timestamp(skb);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (len == skb->len) {
2218c2ecf20Sopenharmony_ci		dev->stats.tx_packets++;
2228c2ecf20Sopenharmony_ci		dev->stats.tx_bytes += skb->len;
2238c2ecf20Sopenharmony_ci		netif_trans_update(dev);
2248c2ecf20Sopenharmony_ci		netif_start_queue(dev);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		/* this is normally done in the interrupt when tx finishes */
2278c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci	else if (len == 0) {
2308c2ecf20Sopenharmony_ci		netif_start_queue(dev);
2318c2ecf20Sopenharmony_ci		dev->stats.tx_dropped++;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci	else {
2348c2ecf20Sopenharmony_ci		netif_start_queue(dev);
2358c2ecf20Sopenharmony_ci		printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len);
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	dev_consume_skb_any(skb);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void uml_net_set_multicast_list(struct net_device *dev)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	return;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic void uml_net_tx_timeout(struct net_device *dev, unsigned int txqueue)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	netif_trans_update(dev);
2538c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
2578c2ecf20Sopenharmony_cistatic void uml_net_poll_controller(struct net_device *dev)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	disable_irq(dev->irq);
2608c2ecf20Sopenharmony_ci	uml_net_interrupt(dev->irq, dev);
2618c2ecf20Sopenharmony_ci	enable_irq(dev->irq);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci#endif
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void uml_net_get_drvinfo(struct net_device *dev,
2668c2ecf20Sopenharmony_ci				struct ethtool_drvinfo *info)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic const struct ethtool_ops uml_net_ethtool_ops = {
2728c2ecf20Sopenharmony_ci	.get_drvinfo	= uml_net_get_drvinfo,
2738c2ecf20Sopenharmony_ci	.get_link	= ethtool_op_get_link,
2748c2ecf20Sopenharmony_ci	.get_ts_info	= ethtool_op_get_ts_info,
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_civoid uml_net_setup_etheraddr(struct net_device *dev, char *str)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	unsigned char *addr = dev->dev_addr;
2808c2ecf20Sopenharmony_ci	char *end;
2818c2ecf20Sopenharmony_ci	int i;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (str == NULL)
2848c2ecf20Sopenharmony_ci		goto random;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++) {
2878c2ecf20Sopenharmony_ci		addr[i] = simple_strtoul(str, &end, 16);
2888c2ecf20Sopenharmony_ci		if ((end == str) ||
2898c2ecf20Sopenharmony_ci		   ((*end != ':') && (*end != ',') && (*end != '\0'))) {
2908c2ecf20Sopenharmony_ci			printk(KERN_ERR
2918c2ecf20Sopenharmony_ci			       "setup_etheraddr: failed to parse '%s' "
2928c2ecf20Sopenharmony_ci			       "as an ethernet address\n", str);
2938c2ecf20Sopenharmony_ci			goto random;
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci		str = end + 1;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci	if (is_multicast_ether_addr(addr)) {
2988c2ecf20Sopenharmony_ci		printk(KERN_ERR
2998c2ecf20Sopenharmony_ci		       "Attempt to assign a multicast ethernet address to a "
3008c2ecf20Sopenharmony_ci		       "device disallowed\n");
3018c2ecf20Sopenharmony_ci		goto random;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(addr)) {
3048c2ecf20Sopenharmony_ci		printk(KERN_ERR
3058c2ecf20Sopenharmony_ci		       "Attempt to assign an invalid ethernet address to a "
3068c2ecf20Sopenharmony_ci		       "device disallowed\n");
3078c2ecf20Sopenharmony_ci		goto random;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci	if (!is_local_ether_addr(addr)) {
3108c2ecf20Sopenharmony_ci		printk(KERN_WARNING
3118c2ecf20Sopenharmony_ci		       "Warning: Assigning a globally valid ethernet "
3128c2ecf20Sopenharmony_ci		       "address to a device\n");
3138c2ecf20Sopenharmony_ci		printk(KERN_WARNING "You should set the 2nd rightmost bit in "
3148c2ecf20Sopenharmony_ci		       "the first byte of the MAC,\n");
3158c2ecf20Sopenharmony_ci		printk(KERN_WARNING "i.e. %02x:%02x:%02x:%02x:%02x:%02x\n",
3168c2ecf20Sopenharmony_ci		       addr[0] | 0x02, addr[1], addr[2], addr[3], addr[4],
3178c2ecf20Sopenharmony_ci		       addr[5]);
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	return;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cirandom:
3228c2ecf20Sopenharmony_ci	printk(KERN_INFO
3238c2ecf20Sopenharmony_ci	       "Choosing a random ethernet address for device %s\n", dev->name);
3248c2ecf20Sopenharmony_ci	eth_hw_addr_random(dev);
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(devices_lock);
3288c2ecf20Sopenharmony_cistatic LIST_HEAD(devices);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic struct platform_driver uml_net_driver = {
3318c2ecf20Sopenharmony_ci	.driver = {
3328c2ecf20Sopenharmony_ci		.name  = DRIVER_NAME,
3338c2ecf20Sopenharmony_ci	},
3348c2ecf20Sopenharmony_ci};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void net_device_release(struct device *dev)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct uml_net *device = dev_get_drvdata(dev);
3398c2ecf20Sopenharmony_ci	struct net_device *netdev = device->dev;
3408c2ecf20Sopenharmony_ci	struct uml_net_private *lp = netdev_priv(netdev);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (lp->remove != NULL)
3438c2ecf20Sopenharmony_ci		(*lp->remove)(&lp->user);
3448c2ecf20Sopenharmony_ci	list_del(&device->list);
3458c2ecf20Sopenharmony_ci	kfree(device);
3468c2ecf20Sopenharmony_ci	free_netdev(netdev);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic const struct net_device_ops uml_netdev_ops = {
3508c2ecf20Sopenharmony_ci	.ndo_open 		= uml_net_open,
3518c2ecf20Sopenharmony_ci	.ndo_stop 		= uml_net_close,
3528c2ecf20Sopenharmony_ci	.ndo_start_xmit 	= uml_net_start_xmit,
3538c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= uml_net_set_multicast_list,
3548c2ecf20Sopenharmony_ci	.ndo_tx_timeout 	= uml_net_tx_timeout,
3558c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
3568c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
3578c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
3588c2ecf20Sopenharmony_ci	.ndo_poll_controller = uml_net_poll_controller,
3598c2ecf20Sopenharmony_ci#endif
3608c2ecf20Sopenharmony_ci};
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/*
3638c2ecf20Sopenharmony_ci * Ensures that platform_driver_register is called only once by
3648c2ecf20Sopenharmony_ci * eth_configure.  Will be set in an initcall.
3658c2ecf20Sopenharmony_ci */
3668c2ecf20Sopenharmony_cistatic int driver_registered;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void eth_configure(int n, void *init, char *mac,
3698c2ecf20Sopenharmony_ci			  struct transport *transport, gfp_t gfp_mask)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct uml_net *device;
3728c2ecf20Sopenharmony_ci	struct net_device *dev;
3738c2ecf20Sopenharmony_ci	struct uml_net_private *lp;
3748c2ecf20Sopenharmony_ci	int err, size;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	size = transport->private_size + sizeof(struct uml_net_private);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	device = kzalloc(sizeof(*device), gfp_mask);
3798c2ecf20Sopenharmony_ci	if (device == NULL) {
3808c2ecf20Sopenharmony_ci		printk(KERN_ERR "eth_configure failed to allocate struct "
3818c2ecf20Sopenharmony_ci		       "uml_net\n");
3828c2ecf20Sopenharmony_ci		return;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	dev = alloc_etherdev(size);
3868c2ecf20Sopenharmony_ci	if (dev == NULL) {
3878c2ecf20Sopenharmony_ci		printk(KERN_ERR "eth_configure: failed to allocate struct "
3888c2ecf20Sopenharmony_ci		       "net_device for eth%d\n", n);
3898c2ecf20Sopenharmony_ci		goto out_free_device;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&device->list);
3938c2ecf20Sopenharmony_ci	device->index = n;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* If this name ends up conflicting with an existing registered
3968c2ecf20Sopenharmony_ci	 * netdevice, that is OK, register_netdev{,ice}() will notice this
3978c2ecf20Sopenharmony_ci	 * and fail.
3988c2ecf20Sopenharmony_ci	 */
3998c2ecf20Sopenharmony_ci	snprintf(dev->name, sizeof(dev->name), "eth%d", n);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	uml_net_setup_etheraddr(dev, mac);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	printk(KERN_INFO "Netdevice %d (%pM) : ", n, dev->dev_addr);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	lp = netdev_priv(dev);
4068c2ecf20Sopenharmony_ci	/* This points to the transport private data. It's still clear, but we
4078c2ecf20Sopenharmony_ci	 * must memset it to 0 *now*. Let's help the drivers. */
4088c2ecf20Sopenharmony_ci	memset(lp, 0, size);
4098c2ecf20Sopenharmony_ci	INIT_WORK(&lp->work, uml_dev_close);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* sysfs register */
4128c2ecf20Sopenharmony_ci	if (!driver_registered) {
4138c2ecf20Sopenharmony_ci		platform_driver_register(&uml_net_driver);
4148c2ecf20Sopenharmony_ci		driver_registered = 1;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci	device->pdev.id = n;
4178c2ecf20Sopenharmony_ci	device->pdev.name = DRIVER_NAME;
4188c2ecf20Sopenharmony_ci	device->pdev.dev.release = net_device_release;
4198c2ecf20Sopenharmony_ci	dev_set_drvdata(&device->pdev.dev, device);
4208c2ecf20Sopenharmony_ci	if (platform_device_register(&device->pdev))
4218c2ecf20Sopenharmony_ci		goto out_free_netdev;
4228c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev,&device->pdev.dev);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	device->dev = dev;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/*
4278c2ecf20Sopenharmony_ci	 * These just fill in a data structure, so there's no failure
4288c2ecf20Sopenharmony_ci	 * to be worried about.
4298c2ecf20Sopenharmony_ci	 */
4308c2ecf20Sopenharmony_ci	(*transport->kern->init)(dev, init);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	*lp = ((struct uml_net_private)
4338c2ecf20Sopenharmony_ci		{ .list  		= LIST_HEAD_INIT(lp->list),
4348c2ecf20Sopenharmony_ci		  .dev 			= dev,
4358c2ecf20Sopenharmony_ci		  .fd 			= -1,
4368c2ecf20Sopenharmony_ci		  .mac 			= { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0},
4378c2ecf20Sopenharmony_ci		  .max_packet		= transport->user->max_packet,
4388c2ecf20Sopenharmony_ci		  .protocol 		= transport->kern->protocol,
4398c2ecf20Sopenharmony_ci		  .open 		= transport->user->open,
4408c2ecf20Sopenharmony_ci		  .close 		= transport->user->close,
4418c2ecf20Sopenharmony_ci		  .remove 		= transport->user->remove,
4428c2ecf20Sopenharmony_ci		  .read 		= transport->kern->read,
4438c2ecf20Sopenharmony_ci		  .write 		= transport->kern->write,
4448c2ecf20Sopenharmony_ci		  .add_address 		= transport->user->add_address,
4458c2ecf20Sopenharmony_ci		  .delete_address  	= transport->user->delete_address });
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	spin_lock_init(&lp->lock);
4488c2ecf20Sopenharmony_ci	memcpy(lp->mac, dev->dev_addr, sizeof(lp->mac));
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if ((transport->user->init != NULL) &&
4518c2ecf20Sopenharmony_ci	    ((*transport->user->init)(&lp->user, dev) != 0))
4528c2ecf20Sopenharmony_ci		goto out_unregister;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	dev->mtu = transport->user->mtu;
4558c2ecf20Sopenharmony_ci	dev->netdev_ops = &uml_netdev_ops;
4568c2ecf20Sopenharmony_ci	dev->ethtool_ops = &uml_net_ethtool_ops;
4578c2ecf20Sopenharmony_ci	dev->watchdog_timeo = (HZ >> 1);
4588c2ecf20Sopenharmony_ci	dev->irq = UM_ETH_IRQ;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	err = update_drop_skb(lp->max_packet);
4618c2ecf20Sopenharmony_ci	if (err)
4628c2ecf20Sopenharmony_ci		goto out_undo_user_init;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	rtnl_lock();
4658c2ecf20Sopenharmony_ci	err = register_netdevice(dev);
4668c2ecf20Sopenharmony_ci	rtnl_unlock();
4678c2ecf20Sopenharmony_ci	if (err)
4688c2ecf20Sopenharmony_ci		goto out_undo_user_init;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	spin_lock(&devices_lock);
4718c2ecf20Sopenharmony_ci	list_add(&device->list, &devices);
4728c2ecf20Sopenharmony_ci	spin_unlock(&devices_lock);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	return;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ciout_undo_user_init:
4778c2ecf20Sopenharmony_ci	if (transport->user->remove != NULL)
4788c2ecf20Sopenharmony_ci		(*transport->user->remove)(&lp->user);
4798c2ecf20Sopenharmony_ciout_unregister:
4808c2ecf20Sopenharmony_ci	platform_device_unregister(&device->pdev);
4818c2ecf20Sopenharmony_ci	return; /* platform_device_unregister frees dev and device */
4828c2ecf20Sopenharmony_ciout_free_netdev:
4838c2ecf20Sopenharmony_ci	free_netdev(dev);
4848c2ecf20Sopenharmony_ciout_free_device:
4858c2ecf20Sopenharmony_ci	kfree(device);
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic struct uml_net *find_device(int n)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct uml_net *device;
4918c2ecf20Sopenharmony_ci	struct list_head *ele;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	spin_lock(&devices_lock);
4948c2ecf20Sopenharmony_ci	list_for_each(ele, &devices) {
4958c2ecf20Sopenharmony_ci		device = list_entry(ele, struct uml_net, list);
4968c2ecf20Sopenharmony_ci		if (device->index == n)
4978c2ecf20Sopenharmony_ci			goto out;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci	device = NULL;
5008c2ecf20Sopenharmony_ci out:
5018c2ecf20Sopenharmony_ci	spin_unlock(&devices_lock);
5028c2ecf20Sopenharmony_ci	return device;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic int eth_parse(char *str, int *index_out, char **str_out,
5068c2ecf20Sopenharmony_ci		     char **error_out)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	char *end;
5098c2ecf20Sopenharmony_ci	int n, err = -EINVAL;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	n = simple_strtoul(str, &end, 0);
5128c2ecf20Sopenharmony_ci	if (end == str) {
5138c2ecf20Sopenharmony_ci		*error_out = "Bad device number";
5148c2ecf20Sopenharmony_ci		return err;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	str = end;
5188c2ecf20Sopenharmony_ci	if (*str != '=') {
5198c2ecf20Sopenharmony_ci		*error_out = "Expected '=' after device number";
5208c2ecf20Sopenharmony_ci		return err;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	str++;
5248c2ecf20Sopenharmony_ci	if (find_device(n)) {
5258c2ecf20Sopenharmony_ci		*error_out = "Device already configured";
5268c2ecf20Sopenharmony_ci		return err;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	*index_out = n;
5308c2ecf20Sopenharmony_ci	*str_out = str;
5318c2ecf20Sopenharmony_ci	return 0;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistruct eth_init {
5358c2ecf20Sopenharmony_ci	struct list_head list;
5368c2ecf20Sopenharmony_ci	char *init;
5378c2ecf20Sopenharmony_ci	int index;
5388c2ecf20Sopenharmony_ci};
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(transports_lock);
5418c2ecf20Sopenharmony_cistatic LIST_HEAD(transports);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci/* Filled in during early boot */
5448c2ecf20Sopenharmony_cistatic LIST_HEAD(eth_cmd_line);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic int check_transport(struct transport *transport, char *eth, int n,
5478c2ecf20Sopenharmony_ci			   void **init_out, char **mac_out, gfp_t gfp_mask)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	int len;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	len = strlen(transport->name);
5528c2ecf20Sopenharmony_ci	if (strncmp(eth, transport->name, len))
5538c2ecf20Sopenharmony_ci		return 0;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	eth += len;
5568c2ecf20Sopenharmony_ci	if (*eth == ',')
5578c2ecf20Sopenharmony_ci		eth++;
5588c2ecf20Sopenharmony_ci	else if (*eth != '\0')
5598c2ecf20Sopenharmony_ci		return 0;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	*init_out = kmalloc(transport->setup_size, gfp_mask);
5628c2ecf20Sopenharmony_ci	if (*init_out == NULL)
5638c2ecf20Sopenharmony_ci		return 1;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (!transport->setup(eth, mac_out, *init_out)) {
5668c2ecf20Sopenharmony_ci		kfree(*init_out);
5678c2ecf20Sopenharmony_ci		*init_out = NULL;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci	return 1;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_civoid register_transport(struct transport *new)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	struct list_head *ele, *next;
5758c2ecf20Sopenharmony_ci	struct eth_init *eth;
5768c2ecf20Sopenharmony_ci	void *init;
5778c2ecf20Sopenharmony_ci	char *mac = NULL;
5788c2ecf20Sopenharmony_ci	int match;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	spin_lock(&transports_lock);
5818c2ecf20Sopenharmony_ci	BUG_ON(!list_empty(&new->list));
5828c2ecf20Sopenharmony_ci	list_add(&new->list, &transports);
5838c2ecf20Sopenharmony_ci	spin_unlock(&transports_lock);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	list_for_each_safe(ele, next, &eth_cmd_line) {
5868c2ecf20Sopenharmony_ci		eth = list_entry(ele, struct eth_init, list);
5878c2ecf20Sopenharmony_ci		match = check_transport(new, eth->init, eth->index, &init,
5888c2ecf20Sopenharmony_ci					&mac, GFP_KERNEL);
5898c2ecf20Sopenharmony_ci		if (!match)
5908c2ecf20Sopenharmony_ci			continue;
5918c2ecf20Sopenharmony_ci		else if (init != NULL) {
5928c2ecf20Sopenharmony_ci			eth_configure(eth->index, init, mac, new, GFP_KERNEL);
5938c2ecf20Sopenharmony_ci			kfree(init);
5948c2ecf20Sopenharmony_ci		}
5958c2ecf20Sopenharmony_ci		list_del(&eth->list);
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic int eth_setup_common(char *str, int index)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	struct list_head *ele;
6028c2ecf20Sopenharmony_ci	struct transport *transport;
6038c2ecf20Sopenharmony_ci	void *init;
6048c2ecf20Sopenharmony_ci	char *mac = NULL;
6058c2ecf20Sopenharmony_ci	int found = 0;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	spin_lock(&transports_lock);
6088c2ecf20Sopenharmony_ci	list_for_each(ele, &transports) {
6098c2ecf20Sopenharmony_ci		transport = list_entry(ele, struct transport, list);
6108c2ecf20Sopenharmony_ci	        if (!check_transport(transport, str, index, &init,
6118c2ecf20Sopenharmony_ci					&mac, GFP_ATOMIC))
6128c2ecf20Sopenharmony_ci			continue;
6138c2ecf20Sopenharmony_ci		if (init != NULL) {
6148c2ecf20Sopenharmony_ci			eth_configure(index, init, mac, transport, GFP_ATOMIC);
6158c2ecf20Sopenharmony_ci			kfree(init);
6168c2ecf20Sopenharmony_ci		}
6178c2ecf20Sopenharmony_ci		found = 1;
6188c2ecf20Sopenharmony_ci		break;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	spin_unlock(&transports_lock);
6228c2ecf20Sopenharmony_ci	return found;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic int __init eth_setup(char *str)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct eth_init *new;
6288c2ecf20Sopenharmony_ci	char *error;
6298c2ecf20Sopenharmony_ci	int n, err;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	err = eth_parse(str, &n, &str, &error);
6328c2ecf20Sopenharmony_ci	if (err) {
6338c2ecf20Sopenharmony_ci		printk(KERN_ERR "eth_setup - Couldn't parse '%s' : %s\n",
6348c2ecf20Sopenharmony_ci		       str, error);
6358c2ecf20Sopenharmony_ci		return 1;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	new = memblock_alloc(sizeof(*new), SMP_CACHE_BYTES);
6398c2ecf20Sopenharmony_ci	if (!new)
6408c2ecf20Sopenharmony_ci		panic("%s: Failed to allocate %zu bytes\n", __func__,
6418c2ecf20Sopenharmony_ci		      sizeof(*new));
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&new->list);
6448c2ecf20Sopenharmony_ci	new->index = n;
6458c2ecf20Sopenharmony_ci	new->init = str;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	list_add_tail(&new->list, &eth_cmd_line);
6488c2ecf20Sopenharmony_ci	return 1;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci__setup("eth", eth_setup);
6528c2ecf20Sopenharmony_ci__uml_help(eth_setup,
6538c2ecf20Sopenharmony_ci"eth[0-9]+=<transport>,<options>\n"
6548c2ecf20Sopenharmony_ci"    Configure a network device.\n\n"
6558c2ecf20Sopenharmony_ci);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic int net_config(char *str, char **error_out)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	int n, err;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	err = eth_parse(str, &n, &str, error_out);
6628c2ecf20Sopenharmony_ci	if (err)
6638c2ecf20Sopenharmony_ci		return err;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* This string is broken up and the pieces used by the underlying
6668c2ecf20Sopenharmony_ci	 * driver.  So, it is freed only if eth_setup_common fails.
6678c2ecf20Sopenharmony_ci	 */
6688c2ecf20Sopenharmony_ci	str = kstrdup(str, GFP_KERNEL);
6698c2ecf20Sopenharmony_ci	if (str == NULL) {
6708c2ecf20Sopenharmony_ci	        *error_out = "net_config failed to strdup string";
6718c2ecf20Sopenharmony_ci		return -ENOMEM;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci	err = !eth_setup_common(str, n);
6748c2ecf20Sopenharmony_ci	if (err)
6758c2ecf20Sopenharmony_ci		kfree(str);
6768c2ecf20Sopenharmony_ci	return err;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic int net_id(char **str, int *start_out, int *end_out)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	char *end;
6828c2ecf20Sopenharmony_ci	int n;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	n = simple_strtoul(*str, &end, 0);
6858c2ecf20Sopenharmony_ci	if ((*end != '\0') || (end == *str))
6868c2ecf20Sopenharmony_ci		return -1;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	*start_out = n;
6898c2ecf20Sopenharmony_ci	*end_out = n;
6908c2ecf20Sopenharmony_ci	*str = end;
6918c2ecf20Sopenharmony_ci	return n;
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic int net_remove(int n, char **error_out)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	struct uml_net *device;
6978c2ecf20Sopenharmony_ci	struct net_device *dev;
6988c2ecf20Sopenharmony_ci	struct uml_net_private *lp;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	device = find_device(n);
7018c2ecf20Sopenharmony_ci	if (device == NULL)
7028c2ecf20Sopenharmony_ci		return -ENODEV;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	dev = device->dev;
7058c2ecf20Sopenharmony_ci	lp = netdev_priv(dev);
7068c2ecf20Sopenharmony_ci	if (lp->fd > 0)
7078c2ecf20Sopenharmony_ci		return -EBUSY;
7088c2ecf20Sopenharmony_ci	unregister_netdev(dev);
7098c2ecf20Sopenharmony_ci	platform_device_unregister(&device->pdev);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	return 0;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_cistatic struct mc_device net_mc = {
7158c2ecf20Sopenharmony_ci	.list		= LIST_HEAD_INIT(net_mc.list),
7168c2ecf20Sopenharmony_ci	.name		= "eth",
7178c2ecf20Sopenharmony_ci	.config		= net_config,
7188c2ecf20Sopenharmony_ci	.get_config	= NULL,
7198c2ecf20Sopenharmony_ci	.id		= net_id,
7208c2ecf20Sopenharmony_ci	.remove		= net_remove,
7218c2ecf20Sopenharmony_ci};
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci#ifdef CONFIG_INET
7248c2ecf20Sopenharmony_cistatic int uml_inetaddr_event(struct notifier_block *this, unsigned long event,
7258c2ecf20Sopenharmony_ci			      void *ptr)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	struct in_ifaddr *ifa = ptr;
7288c2ecf20Sopenharmony_ci	struct net_device *dev = ifa->ifa_dev->dev;
7298c2ecf20Sopenharmony_ci	struct uml_net_private *lp;
7308c2ecf20Sopenharmony_ci	void (*proc)(unsigned char *, unsigned char *, void *);
7318c2ecf20Sopenharmony_ci	unsigned char addr_buf[4], netmask_buf[4];
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	if (dev->netdev_ops->ndo_open != uml_net_open)
7348c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	lp = netdev_priv(dev);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	proc = NULL;
7398c2ecf20Sopenharmony_ci	switch (event) {
7408c2ecf20Sopenharmony_ci	case NETDEV_UP:
7418c2ecf20Sopenharmony_ci		proc = lp->add_address;
7428c2ecf20Sopenharmony_ci		break;
7438c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
7448c2ecf20Sopenharmony_ci		proc = lp->delete_address;
7458c2ecf20Sopenharmony_ci		break;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci	if (proc != NULL) {
7488c2ecf20Sopenharmony_ci		memcpy(addr_buf, &ifa->ifa_address, sizeof(addr_buf));
7498c2ecf20Sopenharmony_ci		memcpy(netmask_buf, &ifa->ifa_mask, sizeof(netmask_buf));
7508c2ecf20Sopenharmony_ci		(*proc)(addr_buf, netmask_buf, &lp->user);
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci/* uml_net_init shouldn't be called twice on two CPUs at the same time */
7568c2ecf20Sopenharmony_cistatic struct notifier_block uml_inetaddr_notifier = {
7578c2ecf20Sopenharmony_ci	.notifier_call		= uml_inetaddr_event,
7588c2ecf20Sopenharmony_ci};
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic void inet_register(void)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	struct list_head *ele;
7638c2ecf20Sopenharmony_ci	struct uml_net_private *lp;
7648c2ecf20Sopenharmony_ci	struct in_device *ip;
7658c2ecf20Sopenharmony_ci	struct in_ifaddr *in;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	register_inetaddr_notifier(&uml_inetaddr_notifier);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	/* Devices may have been opened already, so the uml_inetaddr_notifier
7708c2ecf20Sopenharmony_ci	 * didn't get a chance to run for them.  This fakes it so that
7718c2ecf20Sopenharmony_ci	 * addresses which have already been set up get handled properly.
7728c2ecf20Sopenharmony_ci	 */
7738c2ecf20Sopenharmony_ci	spin_lock(&opened_lock);
7748c2ecf20Sopenharmony_ci	list_for_each(ele, &opened) {
7758c2ecf20Sopenharmony_ci		lp = list_entry(ele, struct uml_net_private, list);
7768c2ecf20Sopenharmony_ci		ip = lp->dev->ip_ptr;
7778c2ecf20Sopenharmony_ci		if (ip == NULL)
7788c2ecf20Sopenharmony_ci			continue;
7798c2ecf20Sopenharmony_ci		in = ip->ifa_list;
7808c2ecf20Sopenharmony_ci		while (in != NULL) {
7818c2ecf20Sopenharmony_ci			uml_inetaddr_event(NULL, NETDEV_UP, in);
7828c2ecf20Sopenharmony_ci			in = in->ifa_next;
7838c2ecf20Sopenharmony_ci		}
7848c2ecf20Sopenharmony_ci	}
7858c2ecf20Sopenharmony_ci	spin_unlock(&opened_lock);
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci#else
7888c2ecf20Sopenharmony_cistatic inline void inet_register(void)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci#endif
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic int uml_net_init(void)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	mconsole_register_dev(&net_mc);
7968c2ecf20Sopenharmony_ci	inet_register();
7978c2ecf20Sopenharmony_ci	return 0;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci__initcall(uml_net_init);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic void close_devices(void)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	struct list_head *ele;
8058c2ecf20Sopenharmony_ci	struct uml_net_private *lp;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	spin_lock(&opened_lock);
8088c2ecf20Sopenharmony_ci	list_for_each(ele, &opened) {
8098c2ecf20Sopenharmony_ci		lp = list_entry(ele, struct uml_net_private, list);
8108c2ecf20Sopenharmony_ci		um_free_irq(lp->dev->irq, lp->dev);
8118c2ecf20Sopenharmony_ci		if ((lp->close != NULL) && (lp->fd >= 0))
8128c2ecf20Sopenharmony_ci			(*lp->close)(lp->fd, &lp->user);
8138c2ecf20Sopenharmony_ci		if (lp->remove != NULL)
8148c2ecf20Sopenharmony_ci			(*lp->remove)(&lp->user);
8158c2ecf20Sopenharmony_ci	}
8168c2ecf20Sopenharmony_ci	spin_unlock(&opened_lock);
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci__uml_exitcall(close_devices);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_civoid iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *,
8228c2ecf20Sopenharmony_ci					void *),
8238c2ecf20Sopenharmony_ci		    void *arg)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	struct net_device *dev = d;
8268c2ecf20Sopenharmony_ci	struct in_device *ip = dev->ip_ptr;
8278c2ecf20Sopenharmony_ci	struct in_ifaddr *in;
8288c2ecf20Sopenharmony_ci	unsigned char address[4], netmask[4];
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (ip == NULL) return;
8318c2ecf20Sopenharmony_ci	in = ip->ifa_list;
8328c2ecf20Sopenharmony_ci	while (in != NULL) {
8338c2ecf20Sopenharmony_ci		memcpy(address, &in->ifa_address, sizeof(address));
8348c2ecf20Sopenharmony_ci		memcpy(netmask, &in->ifa_mask, sizeof(netmask));
8358c2ecf20Sopenharmony_ci		(*cb)(address, netmask, arg);
8368c2ecf20Sopenharmony_ci		in = in->ifa_next;
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ciint dev_netmask(void *d, void *m)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	struct net_device *dev = d;
8438c2ecf20Sopenharmony_ci	struct in_device *ip = dev->ip_ptr;
8448c2ecf20Sopenharmony_ci	struct in_ifaddr *in;
8458c2ecf20Sopenharmony_ci	__be32 *mask_out = m;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (ip == NULL)
8488c2ecf20Sopenharmony_ci		return 1;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	in = ip->ifa_list;
8518c2ecf20Sopenharmony_ci	if (in == NULL)
8528c2ecf20Sopenharmony_ci		return 1;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	*mask_out = in->ifa_mask;
8558c2ecf20Sopenharmony_ci	return 0;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_civoid *get_output_buffer(int *len_out)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	void *ret;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	ret = (void *) __get_free_pages(GFP_KERNEL, 0);
8638c2ecf20Sopenharmony_ci	if (ret) *len_out = PAGE_SIZE;
8648c2ecf20Sopenharmony_ci	else *len_out = 0;
8658c2ecf20Sopenharmony_ci	return ret;
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_civoid free_output_buffer(void *buffer)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	free_pages((unsigned long) buffer, 0);
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ciint tap_setup_common(char *str, char *type, char **dev_name, char **mac_out,
8748c2ecf20Sopenharmony_ci		     char **gate_addr)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	char *remain;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	remain = split_if_spec(str, dev_name, mac_out, gate_addr, NULL);
8798c2ecf20Sopenharmony_ci	if (remain != NULL) {
8808c2ecf20Sopenharmony_ci		printk(KERN_ERR "tap_setup_common - Extra garbage on "
8818c2ecf20Sopenharmony_ci		       "specification : '%s'\n", remain);
8828c2ecf20Sopenharmony_ci		return 1;
8838c2ecf20Sopenharmony_ci	}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	return 0;
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ciunsigned short eth_protocol(struct sk_buff *skb)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	return eth_type_trans(skb, skb->dev);
8918c2ecf20Sopenharmony_ci}
892