162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 462306a36Sopenharmony_ci * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 562306a36Sopenharmony_ci * James Leu (jleu@mindspring.net). 662306a36Sopenharmony_ci * Copyright (C) 2001 by various other people who didn't put their name here. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/memblock.h> 1062306a36Sopenharmony_ci#include <linux/etherdevice.h> 1162306a36Sopenharmony_ci#include <linux/ethtool.h> 1262306a36Sopenharmony_ci#include <linux/inetdevice.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/list.h> 1562306a36Sopenharmony_ci#include <linux/netdevice.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1862306a36Sopenharmony_ci#include <linux/skbuff.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/spinlock.h> 2162306a36Sopenharmony_ci#include <init.h> 2262306a36Sopenharmony_ci#include <irq_kern.h> 2362306a36Sopenharmony_ci#include <irq_user.h> 2462306a36Sopenharmony_ci#include "mconsole_kern.h" 2562306a36Sopenharmony_ci#include <net_kern.h> 2662306a36Sopenharmony_ci#include <net_user.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define DRIVER_NAME "uml-netdev" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(opened_lock); 3162306a36Sopenharmony_cistatic LIST_HEAD(opened); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * The drop_skb is used when we can't allocate an skb. The 3562306a36Sopenharmony_ci * packet is read into drop_skb in order to get the data off the 3662306a36Sopenharmony_ci * connection to the host. 3762306a36Sopenharmony_ci * It is reallocated whenever a maximum packet size is seen which is 3862306a36Sopenharmony_ci * larger than any seen before. update_drop_skb is called from 3962306a36Sopenharmony_ci * eth_configure when a new interface is added. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(drop_lock); 4262306a36Sopenharmony_cistatic struct sk_buff *drop_skb; 4362306a36Sopenharmony_cistatic int drop_max; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int update_drop_skb(int max) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct sk_buff *new; 4862306a36Sopenharmony_ci unsigned long flags; 4962306a36Sopenharmony_ci int err = 0; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci spin_lock_irqsave(&drop_lock, flags); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (max <= drop_max) 5462306a36Sopenharmony_ci goto out; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci err = -ENOMEM; 5762306a36Sopenharmony_ci new = dev_alloc_skb(max); 5862306a36Sopenharmony_ci if (new == NULL) 5962306a36Sopenharmony_ci goto out; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci skb_put(new, max); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci kfree_skb(drop_skb); 6462306a36Sopenharmony_ci drop_skb = new; 6562306a36Sopenharmony_ci drop_max = max; 6662306a36Sopenharmony_ci err = 0; 6762306a36Sopenharmony_ciout: 6862306a36Sopenharmony_ci spin_unlock_irqrestore(&drop_lock, flags); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return err; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int uml_net_rx(struct net_device *dev) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct uml_net_private *lp = netdev_priv(dev); 7662306a36Sopenharmony_ci int pkt_len; 7762306a36Sopenharmony_ci struct sk_buff *skb; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* If we can't allocate memory, try again next round. */ 8062306a36Sopenharmony_ci skb = dev_alloc_skb(lp->max_packet); 8162306a36Sopenharmony_ci if (skb == NULL) { 8262306a36Sopenharmony_ci drop_skb->dev = dev; 8362306a36Sopenharmony_ci /* Read a packet into drop_skb and don't do anything with it. */ 8462306a36Sopenharmony_ci (*lp->read)(lp->fd, drop_skb, lp); 8562306a36Sopenharmony_ci dev->stats.rx_dropped++; 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci skb->dev = dev; 9062306a36Sopenharmony_ci skb_put(skb, lp->max_packet); 9162306a36Sopenharmony_ci skb_reset_mac_header(skb); 9262306a36Sopenharmony_ci pkt_len = (*lp->read)(lp->fd, skb, lp); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (pkt_len > 0) { 9562306a36Sopenharmony_ci skb_trim(skb, pkt_len); 9662306a36Sopenharmony_ci skb->protocol = (*lp->protocol)(skb); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci dev->stats.rx_bytes += skb->len; 9962306a36Sopenharmony_ci dev->stats.rx_packets++; 10062306a36Sopenharmony_ci netif_rx(skb); 10162306a36Sopenharmony_ci return pkt_len; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci kfree_skb(skb); 10562306a36Sopenharmony_ci return pkt_len; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void uml_dev_close(struct work_struct *work) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct uml_net_private *lp = 11162306a36Sopenharmony_ci container_of(work, struct uml_net_private, work); 11262306a36Sopenharmony_ci dev_close(lp->dev); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic irqreturn_t uml_net_interrupt(int irq, void *dev_id) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct net_device *dev = dev_id; 11862306a36Sopenharmony_ci struct uml_net_private *lp = netdev_priv(dev); 11962306a36Sopenharmony_ci int err; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (!netif_running(dev)) 12262306a36Sopenharmony_ci return IRQ_NONE; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci spin_lock(&lp->lock); 12562306a36Sopenharmony_ci while ((err = uml_net_rx(dev)) > 0) ; 12662306a36Sopenharmony_ci if (err < 0) { 12762306a36Sopenharmony_ci printk(KERN_ERR 12862306a36Sopenharmony_ci "Device '%s' read returned %d, shutting it down\n", 12962306a36Sopenharmony_ci dev->name, err); 13062306a36Sopenharmony_ci /* dev_close can't be called in interrupt context, and takes 13162306a36Sopenharmony_ci * again lp->lock. 13262306a36Sopenharmony_ci * And dev_close() can be safely called multiple times on the 13362306a36Sopenharmony_ci * same device, since it tests for (dev->flags & IFF_UP). So 13462306a36Sopenharmony_ci * there's no harm in delaying the device shutdown. 13562306a36Sopenharmony_ci * Furthermore, the workqueue will not re-enqueue an already 13662306a36Sopenharmony_ci * enqueued work item. */ 13762306a36Sopenharmony_ci schedule_work(&lp->work); 13862306a36Sopenharmony_ci goto out; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ciout: 14162306a36Sopenharmony_ci spin_unlock(&lp->lock); 14262306a36Sopenharmony_ci return IRQ_HANDLED; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int uml_net_open(struct net_device *dev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct uml_net_private *lp = netdev_priv(dev); 14862306a36Sopenharmony_ci int err; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (lp->fd >= 0) { 15162306a36Sopenharmony_ci err = -ENXIO; 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci lp->fd = (*lp->open)(&lp->user); 15662306a36Sopenharmony_ci if (lp->fd < 0) { 15762306a36Sopenharmony_ci err = lp->fd; 15862306a36Sopenharmony_ci goto out; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt, 16262306a36Sopenharmony_ci IRQF_SHARED, dev->name, dev); 16362306a36Sopenharmony_ci if (err < 0) { 16462306a36Sopenharmony_ci printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err); 16562306a36Sopenharmony_ci err = -ENETUNREACH; 16662306a36Sopenharmony_ci goto out_close; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci netif_start_queue(dev); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* clear buffer - it can happen that the host side of the interface 17262306a36Sopenharmony_ci * is full when we get here. In this case, new data is never queued, 17362306a36Sopenharmony_ci * SIGIOs never arrive, and the net never works. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci while ((err = uml_net_rx(dev)) > 0) ; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci spin_lock(&opened_lock); 17862306a36Sopenharmony_ci list_add(&lp->list, &opened); 17962306a36Sopenharmony_ci spin_unlock(&opened_lock); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ciout_close: 18362306a36Sopenharmony_ci if (lp->close != NULL) (*lp->close)(lp->fd, &lp->user); 18462306a36Sopenharmony_ci lp->fd = -1; 18562306a36Sopenharmony_ciout: 18662306a36Sopenharmony_ci return err; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int uml_net_close(struct net_device *dev) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct uml_net_private *lp = netdev_priv(dev); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci netif_stop_queue(dev); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci um_free_irq(dev->irq, dev); 19662306a36Sopenharmony_ci if (lp->close != NULL) 19762306a36Sopenharmony_ci (*lp->close)(lp->fd, &lp->user); 19862306a36Sopenharmony_ci lp->fd = -1; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci spin_lock(&opened_lock); 20162306a36Sopenharmony_ci list_del(&lp->list); 20262306a36Sopenharmony_ci spin_unlock(&opened_lock); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic netdev_tx_t uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct uml_net_private *lp = netdev_priv(dev); 21062306a36Sopenharmony_ci unsigned long flags; 21162306a36Sopenharmony_ci int len; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci netif_stop_queue(dev); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci len = (*lp->write)(lp->fd, skb, lp); 21862306a36Sopenharmony_ci skb_tx_timestamp(skb); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (len == skb->len) { 22162306a36Sopenharmony_ci dev->stats.tx_packets++; 22262306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 22362306a36Sopenharmony_ci netif_trans_update(dev); 22462306a36Sopenharmony_ci netif_start_queue(dev); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* this is normally done in the interrupt when tx finishes */ 22762306a36Sopenharmony_ci netif_wake_queue(dev); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci else if (len == 0) { 23062306a36Sopenharmony_ci netif_start_queue(dev); 23162306a36Sopenharmony_ci dev->stats.tx_dropped++; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci else { 23462306a36Sopenharmony_ci netif_start_queue(dev); 23562306a36Sopenharmony_ci printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci dev_consume_skb_any(skb); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return NETDEV_TX_OK; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void uml_net_set_multicast_list(struct net_device *dev) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci return; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void uml_net_tx_timeout(struct net_device *dev, unsigned int txqueue) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci netif_trans_update(dev); 25362306a36Sopenharmony_ci netif_wake_queue(dev); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 25762306a36Sopenharmony_cistatic void uml_net_poll_controller(struct net_device *dev) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci disable_irq(dev->irq); 26062306a36Sopenharmony_ci uml_net_interrupt(dev->irq, dev); 26162306a36Sopenharmony_ci enable_irq(dev->irq); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci#endif 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void uml_net_get_drvinfo(struct net_device *dev, 26662306a36Sopenharmony_ci struct ethtool_drvinfo *info) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci strscpy(info->driver, DRIVER_NAME, sizeof(info->driver)); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const struct ethtool_ops uml_net_ethtool_ops = { 27262306a36Sopenharmony_ci .get_drvinfo = uml_net_get_drvinfo, 27362306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 27462306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_civoid uml_net_setup_etheraddr(struct net_device *dev, char *str) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 28062306a36Sopenharmony_ci char *end; 28162306a36Sopenharmony_ci int i; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (str == NULL) 28462306a36Sopenharmony_ci goto random; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 28762306a36Sopenharmony_ci addr[i] = simple_strtoul(str, &end, 16); 28862306a36Sopenharmony_ci if ((end == str) || 28962306a36Sopenharmony_ci ((*end != ':') && (*end != ',') && (*end != '\0'))) { 29062306a36Sopenharmony_ci printk(KERN_ERR 29162306a36Sopenharmony_ci "setup_etheraddr: failed to parse '%s' " 29262306a36Sopenharmony_ci "as an ethernet address\n", str); 29362306a36Sopenharmony_ci goto random; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci str = end + 1; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci if (is_multicast_ether_addr(addr)) { 29862306a36Sopenharmony_ci printk(KERN_ERR 29962306a36Sopenharmony_ci "Attempt to assign a multicast ethernet address to a " 30062306a36Sopenharmony_ci "device disallowed\n"); 30162306a36Sopenharmony_ci goto random; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci if (!is_valid_ether_addr(addr)) { 30462306a36Sopenharmony_ci printk(KERN_ERR 30562306a36Sopenharmony_ci "Attempt to assign an invalid ethernet address to a " 30662306a36Sopenharmony_ci "device disallowed\n"); 30762306a36Sopenharmony_ci goto random; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci if (!is_local_ether_addr(addr)) { 31062306a36Sopenharmony_ci printk(KERN_WARNING 31162306a36Sopenharmony_ci "Warning: Assigning a globally valid ethernet " 31262306a36Sopenharmony_ci "address to a device\n"); 31362306a36Sopenharmony_ci printk(KERN_WARNING "You should set the 2nd rightmost bit in " 31462306a36Sopenharmony_ci "the first byte of the MAC,\n"); 31562306a36Sopenharmony_ci printk(KERN_WARNING "i.e. %02x:%02x:%02x:%02x:%02x:%02x\n", 31662306a36Sopenharmony_ci addr[0] | 0x02, addr[1], addr[2], addr[3], addr[4], 31762306a36Sopenharmony_ci addr[5]); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 32062306a36Sopenharmony_ci return; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cirandom: 32362306a36Sopenharmony_ci printk(KERN_INFO 32462306a36Sopenharmony_ci "Choosing a random ethernet address for device %s\n", dev->name); 32562306a36Sopenharmony_ci eth_hw_addr_random(dev); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(devices_lock); 32962306a36Sopenharmony_cistatic LIST_HEAD(devices); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic struct platform_driver uml_net_driver = { 33262306a36Sopenharmony_ci .driver = { 33362306a36Sopenharmony_ci .name = DRIVER_NAME, 33462306a36Sopenharmony_ci }, 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void net_device_release(struct device *dev) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct uml_net *device = dev_get_drvdata(dev); 34062306a36Sopenharmony_ci struct net_device *netdev = device->dev; 34162306a36Sopenharmony_ci struct uml_net_private *lp = netdev_priv(netdev); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (lp->remove != NULL) 34462306a36Sopenharmony_ci (*lp->remove)(&lp->user); 34562306a36Sopenharmony_ci list_del(&device->list); 34662306a36Sopenharmony_ci kfree(device); 34762306a36Sopenharmony_ci free_netdev(netdev); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic const struct net_device_ops uml_netdev_ops = { 35162306a36Sopenharmony_ci .ndo_open = uml_net_open, 35262306a36Sopenharmony_ci .ndo_stop = uml_net_close, 35362306a36Sopenharmony_ci .ndo_start_xmit = uml_net_start_xmit, 35462306a36Sopenharmony_ci .ndo_set_rx_mode = uml_net_set_multicast_list, 35562306a36Sopenharmony_ci .ndo_tx_timeout = uml_net_tx_timeout, 35662306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 35762306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 35862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 35962306a36Sopenharmony_ci .ndo_poll_controller = uml_net_poll_controller, 36062306a36Sopenharmony_ci#endif 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* 36462306a36Sopenharmony_ci * Ensures that platform_driver_register is called only once by 36562306a36Sopenharmony_ci * eth_configure. Will be set in an initcall. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_cistatic int driver_registered; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic void eth_configure(int n, void *init, char *mac, 37062306a36Sopenharmony_ci struct transport *transport, gfp_t gfp_mask) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct uml_net *device; 37362306a36Sopenharmony_ci struct net_device *dev; 37462306a36Sopenharmony_ci struct uml_net_private *lp; 37562306a36Sopenharmony_ci int err, size; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci size = transport->private_size + sizeof(struct uml_net_private); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci device = kzalloc(sizeof(*device), gfp_mask); 38062306a36Sopenharmony_ci if (device == NULL) { 38162306a36Sopenharmony_ci printk(KERN_ERR "eth_configure failed to allocate struct " 38262306a36Sopenharmony_ci "uml_net\n"); 38362306a36Sopenharmony_ci return; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci dev = alloc_etherdev(size); 38762306a36Sopenharmony_ci if (dev == NULL) { 38862306a36Sopenharmony_ci printk(KERN_ERR "eth_configure: failed to allocate struct " 38962306a36Sopenharmony_ci "net_device for eth%d\n", n); 39062306a36Sopenharmony_ci goto out_free_device; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci INIT_LIST_HEAD(&device->list); 39462306a36Sopenharmony_ci device->index = n; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* If this name ends up conflicting with an existing registered 39762306a36Sopenharmony_ci * netdevice, that is OK, register_netdev{,ice}() will notice this 39862306a36Sopenharmony_ci * and fail. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci snprintf(dev->name, sizeof(dev->name), "eth%d", n); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci uml_net_setup_etheraddr(dev, mac); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci printk(KERN_INFO "Netdevice %d (%pM) : ", n, dev->dev_addr); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci lp = netdev_priv(dev); 40762306a36Sopenharmony_ci /* This points to the transport private data. It's still clear, but we 40862306a36Sopenharmony_ci * must memset it to 0 *now*. Let's help the drivers. */ 40962306a36Sopenharmony_ci memset(lp, 0, size); 41062306a36Sopenharmony_ci INIT_WORK(&lp->work, uml_dev_close); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* sysfs register */ 41362306a36Sopenharmony_ci if (!driver_registered) { 41462306a36Sopenharmony_ci platform_driver_register(¨_net_driver); 41562306a36Sopenharmony_ci driver_registered = 1; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci device->pdev.id = n; 41862306a36Sopenharmony_ci device->pdev.name = DRIVER_NAME; 41962306a36Sopenharmony_ci device->pdev.dev.release = net_device_release; 42062306a36Sopenharmony_ci dev_set_drvdata(&device->pdev.dev, device); 42162306a36Sopenharmony_ci if (platform_device_register(&device->pdev)) 42262306a36Sopenharmony_ci goto out_free_netdev; 42362306a36Sopenharmony_ci SET_NETDEV_DEV(dev,&device->pdev.dev); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci device->dev = dev; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* 42862306a36Sopenharmony_ci * These just fill in a data structure, so there's no failure 42962306a36Sopenharmony_ci * to be worried about. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ci (*transport->kern->init)(dev, init); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci *lp = ((struct uml_net_private) 43462306a36Sopenharmony_ci { .list = LIST_HEAD_INIT(lp->list), 43562306a36Sopenharmony_ci .dev = dev, 43662306a36Sopenharmony_ci .fd = -1, 43762306a36Sopenharmony_ci .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0}, 43862306a36Sopenharmony_ci .max_packet = transport->user->max_packet, 43962306a36Sopenharmony_ci .protocol = transport->kern->protocol, 44062306a36Sopenharmony_ci .open = transport->user->open, 44162306a36Sopenharmony_ci .close = transport->user->close, 44262306a36Sopenharmony_ci .remove = transport->user->remove, 44362306a36Sopenharmony_ci .read = transport->kern->read, 44462306a36Sopenharmony_ci .write = transport->kern->write, 44562306a36Sopenharmony_ci .add_address = transport->user->add_address, 44662306a36Sopenharmony_ci .delete_address = transport->user->delete_address }); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci spin_lock_init(&lp->lock); 44962306a36Sopenharmony_ci memcpy(lp->mac, dev->dev_addr, sizeof(lp->mac)); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if ((transport->user->init != NULL) && 45262306a36Sopenharmony_ci ((*transport->user->init)(&lp->user, dev) != 0)) 45362306a36Sopenharmony_ci goto out_unregister; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci dev->mtu = transport->user->mtu; 45662306a36Sopenharmony_ci dev->netdev_ops = ¨_netdev_ops; 45762306a36Sopenharmony_ci dev->ethtool_ops = ¨_net_ethtool_ops; 45862306a36Sopenharmony_ci dev->watchdog_timeo = (HZ >> 1); 45962306a36Sopenharmony_ci dev->irq = UM_ETH_IRQ; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci err = update_drop_skb(lp->max_packet); 46262306a36Sopenharmony_ci if (err) 46362306a36Sopenharmony_ci goto out_undo_user_init; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci rtnl_lock(); 46662306a36Sopenharmony_ci err = register_netdevice(dev); 46762306a36Sopenharmony_ci rtnl_unlock(); 46862306a36Sopenharmony_ci if (err) 46962306a36Sopenharmony_ci goto out_undo_user_init; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci spin_lock(&devices_lock); 47262306a36Sopenharmony_ci list_add(&device->list, &devices); 47362306a36Sopenharmony_ci spin_unlock(&devices_lock); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ciout_undo_user_init: 47862306a36Sopenharmony_ci if (transport->user->remove != NULL) 47962306a36Sopenharmony_ci (*transport->user->remove)(&lp->user); 48062306a36Sopenharmony_ciout_unregister: 48162306a36Sopenharmony_ci platform_device_unregister(&device->pdev); 48262306a36Sopenharmony_ci return; /* platform_device_unregister frees dev and device */ 48362306a36Sopenharmony_ciout_free_netdev: 48462306a36Sopenharmony_ci free_netdev(dev); 48562306a36Sopenharmony_ciout_free_device: 48662306a36Sopenharmony_ci kfree(device); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic struct uml_net *find_device(int n) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct uml_net *device; 49262306a36Sopenharmony_ci struct list_head *ele; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci spin_lock(&devices_lock); 49562306a36Sopenharmony_ci list_for_each(ele, &devices) { 49662306a36Sopenharmony_ci device = list_entry(ele, struct uml_net, list); 49762306a36Sopenharmony_ci if (device->index == n) 49862306a36Sopenharmony_ci goto out; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci device = NULL; 50162306a36Sopenharmony_ci out: 50262306a36Sopenharmony_ci spin_unlock(&devices_lock); 50362306a36Sopenharmony_ci return device; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic int eth_parse(char *str, int *index_out, char **str_out, 50762306a36Sopenharmony_ci char **error_out) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci char *end; 51062306a36Sopenharmony_ci int n, err = -EINVAL; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci n = simple_strtoul(str, &end, 0); 51362306a36Sopenharmony_ci if (end == str) { 51462306a36Sopenharmony_ci *error_out = "Bad device number"; 51562306a36Sopenharmony_ci return err; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci str = end; 51962306a36Sopenharmony_ci if (*str != '=') { 52062306a36Sopenharmony_ci *error_out = "Expected '=' after device number"; 52162306a36Sopenharmony_ci return err; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci str++; 52562306a36Sopenharmony_ci if (find_device(n)) { 52662306a36Sopenharmony_ci *error_out = "Device already configured"; 52762306a36Sopenharmony_ci return err; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci *index_out = n; 53162306a36Sopenharmony_ci *str_out = str; 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistruct eth_init { 53662306a36Sopenharmony_ci struct list_head list; 53762306a36Sopenharmony_ci char *init; 53862306a36Sopenharmony_ci int index; 53962306a36Sopenharmony_ci}; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(transports_lock); 54262306a36Sopenharmony_cistatic LIST_HEAD(transports); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/* Filled in during early boot */ 54562306a36Sopenharmony_cistatic LIST_HEAD(eth_cmd_line); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int check_transport(struct transport *transport, char *eth, int n, 54862306a36Sopenharmony_ci void **init_out, char **mac_out, gfp_t gfp_mask) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci int len; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci len = strlen(transport->name); 55362306a36Sopenharmony_ci if (strncmp(eth, transport->name, len)) 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci eth += len; 55762306a36Sopenharmony_ci if (*eth == ',') 55862306a36Sopenharmony_ci eth++; 55962306a36Sopenharmony_ci else if (*eth != '\0') 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci *init_out = kmalloc(transport->setup_size, gfp_mask); 56362306a36Sopenharmony_ci if (*init_out == NULL) 56462306a36Sopenharmony_ci return 1; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (!transport->setup(eth, mac_out, *init_out)) { 56762306a36Sopenharmony_ci kfree(*init_out); 56862306a36Sopenharmony_ci *init_out = NULL; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci return 1; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_civoid register_transport(struct transport *new) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct list_head *ele, *next; 57662306a36Sopenharmony_ci struct eth_init *eth; 57762306a36Sopenharmony_ci void *init; 57862306a36Sopenharmony_ci char *mac = NULL; 57962306a36Sopenharmony_ci int match; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci spin_lock(&transports_lock); 58262306a36Sopenharmony_ci BUG_ON(!list_empty(&new->list)); 58362306a36Sopenharmony_ci list_add(&new->list, &transports); 58462306a36Sopenharmony_ci spin_unlock(&transports_lock); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci list_for_each_safe(ele, next, ð_cmd_line) { 58762306a36Sopenharmony_ci eth = list_entry(ele, struct eth_init, list); 58862306a36Sopenharmony_ci match = check_transport(new, eth->init, eth->index, &init, 58962306a36Sopenharmony_ci &mac, GFP_KERNEL); 59062306a36Sopenharmony_ci if (!match) 59162306a36Sopenharmony_ci continue; 59262306a36Sopenharmony_ci else if (init != NULL) { 59362306a36Sopenharmony_ci eth_configure(eth->index, init, mac, new, GFP_KERNEL); 59462306a36Sopenharmony_ci kfree(init); 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci list_del(ð->list); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int eth_setup_common(char *str, int index) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct list_head *ele; 60362306a36Sopenharmony_ci struct transport *transport; 60462306a36Sopenharmony_ci void *init; 60562306a36Sopenharmony_ci char *mac = NULL; 60662306a36Sopenharmony_ci int found = 0; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci spin_lock(&transports_lock); 60962306a36Sopenharmony_ci list_for_each(ele, &transports) { 61062306a36Sopenharmony_ci transport = list_entry(ele, struct transport, list); 61162306a36Sopenharmony_ci if (!check_transport(transport, str, index, &init, 61262306a36Sopenharmony_ci &mac, GFP_ATOMIC)) 61362306a36Sopenharmony_ci continue; 61462306a36Sopenharmony_ci if (init != NULL) { 61562306a36Sopenharmony_ci eth_configure(index, init, mac, transport, GFP_ATOMIC); 61662306a36Sopenharmony_ci kfree(init); 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci found = 1; 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci spin_unlock(&transports_lock); 62362306a36Sopenharmony_ci return found; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic int __init eth_setup(char *str) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct eth_init *new; 62962306a36Sopenharmony_ci char *error; 63062306a36Sopenharmony_ci int n, err; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci err = eth_parse(str, &n, &str, &error); 63362306a36Sopenharmony_ci if (err) { 63462306a36Sopenharmony_ci printk(KERN_ERR "eth_setup - Couldn't parse '%s' : %s\n", 63562306a36Sopenharmony_ci str, error); 63662306a36Sopenharmony_ci return 1; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci new = memblock_alloc(sizeof(*new), SMP_CACHE_BYTES); 64062306a36Sopenharmony_ci if (!new) 64162306a36Sopenharmony_ci panic("%s: Failed to allocate %zu bytes\n", __func__, 64262306a36Sopenharmony_ci sizeof(*new)); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci INIT_LIST_HEAD(&new->list); 64562306a36Sopenharmony_ci new->index = n; 64662306a36Sopenharmony_ci new->init = str; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci list_add_tail(&new->list, ð_cmd_line); 64962306a36Sopenharmony_ci return 1; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci__setup("eth", eth_setup); 65362306a36Sopenharmony_ci__uml_help(eth_setup, 65462306a36Sopenharmony_ci"eth[0-9]+=<transport>,<options>\n" 65562306a36Sopenharmony_ci" Configure a network device.\n\n" 65662306a36Sopenharmony_ci); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int net_config(char *str, char **error_out) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci int n, err; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci err = eth_parse(str, &n, &str, error_out); 66362306a36Sopenharmony_ci if (err) 66462306a36Sopenharmony_ci return err; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* This string is broken up and the pieces used by the underlying 66762306a36Sopenharmony_ci * driver. So, it is freed only if eth_setup_common fails. 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ci str = kstrdup(str, GFP_KERNEL); 67062306a36Sopenharmony_ci if (str == NULL) { 67162306a36Sopenharmony_ci *error_out = "net_config failed to strdup string"; 67262306a36Sopenharmony_ci return -ENOMEM; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci err = !eth_setup_common(str, n); 67562306a36Sopenharmony_ci if (err) 67662306a36Sopenharmony_ci kfree(str); 67762306a36Sopenharmony_ci return err; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int net_id(char **str, int *start_out, int *end_out) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci char *end; 68362306a36Sopenharmony_ci int n; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci n = simple_strtoul(*str, &end, 0); 68662306a36Sopenharmony_ci if ((*end != '\0') || (end == *str)) 68762306a36Sopenharmony_ci return -1; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci *start_out = n; 69062306a36Sopenharmony_ci *end_out = n; 69162306a36Sopenharmony_ci *str = end; 69262306a36Sopenharmony_ci return n; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int net_remove(int n, char **error_out) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct uml_net *device; 69862306a36Sopenharmony_ci struct net_device *dev; 69962306a36Sopenharmony_ci struct uml_net_private *lp; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci device = find_device(n); 70262306a36Sopenharmony_ci if (device == NULL) 70362306a36Sopenharmony_ci return -ENODEV; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci dev = device->dev; 70662306a36Sopenharmony_ci lp = netdev_priv(dev); 70762306a36Sopenharmony_ci if (lp->fd > 0) 70862306a36Sopenharmony_ci return -EBUSY; 70962306a36Sopenharmony_ci unregister_netdev(dev); 71062306a36Sopenharmony_ci platform_device_unregister(&device->pdev); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic struct mc_device net_mc = { 71662306a36Sopenharmony_ci .list = LIST_HEAD_INIT(net_mc.list), 71762306a36Sopenharmony_ci .name = "eth", 71862306a36Sopenharmony_ci .config = net_config, 71962306a36Sopenharmony_ci .get_config = NULL, 72062306a36Sopenharmony_ci .id = net_id, 72162306a36Sopenharmony_ci .remove = net_remove, 72262306a36Sopenharmony_ci}; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci#ifdef CONFIG_INET 72562306a36Sopenharmony_cistatic int uml_inetaddr_event(struct notifier_block *this, unsigned long event, 72662306a36Sopenharmony_ci void *ptr) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct in_ifaddr *ifa = ptr; 72962306a36Sopenharmony_ci struct net_device *dev = ifa->ifa_dev->dev; 73062306a36Sopenharmony_ci struct uml_net_private *lp; 73162306a36Sopenharmony_ci void (*proc)(unsigned char *, unsigned char *, void *); 73262306a36Sopenharmony_ci unsigned char addr_buf[4], netmask_buf[4]; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (dev->netdev_ops->ndo_open != uml_net_open) 73562306a36Sopenharmony_ci return NOTIFY_DONE; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci lp = netdev_priv(dev); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci proc = NULL; 74062306a36Sopenharmony_ci switch (event) { 74162306a36Sopenharmony_ci case NETDEV_UP: 74262306a36Sopenharmony_ci proc = lp->add_address; 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci case NETDEV_DOWN: 74562306a36Sopenharmony_ci proc = lp->delete_address; 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci if (proc != NULL) { 74962306a36Sopenharmony_ci memcpy(addr_buf, &ifa->ifa_address, sizeof(addr_buf)); 75062306a36Sopenharmony_ci memcpy(netmask_buf, &ifa->ifa_mask, sizeof(netmask_buf)); 75162306a36Sopenharmony_ci (*proc)(addr_buf, netmask_buf, &lp->user); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci return NOTIFY_DONE; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/* uml_net_init shouldn't be called twice on two CPUs at the same time */ 75762306a36Sopenharmony_cistatic struct notifier_block uml_inetaddr_notifier = { 75862306a36Sopenharmony_ci .notifier_call = uml_inetaddr_event, 75962306a36Sopenharmony_ci}; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic void inet_register(void) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct list_head *ele; 76462306a36Sopenharmony_ci struct uml_net_private *lp; 76562306a36Sopenharmony_ci struct in_device *ip; 76662306a36Sopenharmony_ci struct in_ifaddr *in; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci register_inetaddr_notifier(¨_inetaddr_notifier); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* Devices may have been opened already, so the uml_inetaddr_notifier 77162306a36Sopenharmony_ci * didn't get a chance to run for them. This fakes it so that 77262306a36Sopenharmony_ci * addresses which have already been set up get handled properly. 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_ci spin_lock(&opened_lock); 77562306a36Sopenharmony_ci list_for_each(ele, &opened) { 77662306a36Sopenharmony_ci lp = list_entry(ele, struct uml_net_private, list); 77762306a36Sopenharmony_ci ip = lp->dev->ip_ptr; 77862306a36Sopenharmony_ci if (ip == NULL) 77962306a36Sopenharmony_ci continue; 78062306a36Sopenharmony_ci in = ip->ifa_list; 78162306a36Sopenharmony_ci while (in != NULL) { 78262306a36Sopenharmony_ci uml_inetaddr_event(NULL, NETDEV_UP, in); 78362306a36Sopenharmony_ci in = in->ifa_next; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci spin_unlock(&opened_lock); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci#else 78962306a36Sopenharmony_cistatic inline void inet_register(void) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci#endif 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic int uml_net_init(void) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci mconsole_register_dev(&net_mc); 79762306a36Sopenharmony_ci inet_register(); 79862306a36Sopenharmony_ci return 0; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci__initcall(uml_net_init); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic void close_devices(void) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct list_head *ele; 80662306a36Sopenharmony_ci struct uml_net_private *lp; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci spin_lock(&opened_lock); 80962306a36Sopenharmony_ci list_for_each(ele, &opened) { 81062306a36Sopenharmony_ci lp = list_entry(ele, struct uml_net_private, list); 81162306a36Sopenharmony_ci um_free_irq(lp->dev->irq, lp->dev); 81262306a36Sopenharmony_ci if ((lp->close != NULL) && (lp->fd >= 0)) 81362306a36Sopenharmony_ci (*lp->close)(lp->fd, &lp->user); 81462306a36Sopenharmony_ci if (lp->remove != NULL) 81562306a36Sopenharmony_ci (*lp->remove)(&lp->user); 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci spin_unlock(&opened_lock); 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci__uml_exitcall(close_devices); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_civoid iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *, 82362306a36Sopenharmony_ci void *), 82462306a36Sopenharmony_ci void *arg) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct net_device *dev = d; 82762306a36Sopenharmony_ci struct in_device *ip = dev->ip_ptr; 82862306a36Sopenharmony_ci struct in_ifaddr *in; 82962306a36Sopenharmony_ci unsigned char address[4], netmask[4]; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (ip == NULL) return; 83262306a36Sopenharmony_ci in = ip->ifa_list; 83362306a36Sopenharmony_ci while (in != NULL) { 83462306a36Sopenharmony_ci memcpy(address, &in->ifa_address, sizeof(address)); 83562306a36Sopenharmony_ci memcpy(netmask, &in->ifa_mask, sizeof(netmask)); 83662306a36Sopenharmony_ci (*cb)(address, netmask, arg); 83762306a36Sopenharmony_ci in = in->ifa_next; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ciint dev_netmask(void *d, void *m) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci struct net_device *dev = d; 84462306a36Sopenharmony_ci struct in_device *ip = dev->ip_ptr; 84562306a36Sopenharmony_ci struct in_ifaddr *in; 84662306a36Sopenharmony_ci __be32 *mask_out = m; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (ip == NULL) 84962306a36Sopenharmony_ci return 1; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci in = ip->ifa_list; 85262306a36Sopenharmony_ci if (in == NULL) 85362306a36Sopenharmony_ci return 1; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci *mask_out = in->ifa_mask; 85662306a36Sopenharmony_ci return 0; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_civoid *get_output_buffer(int *len_out) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci void *ret; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci ret = (void *) __get_free_pages(GFP_KERNEL, 0); 86462306a36Sopenharmony_ci if (ret) *len_out = PAGE_SIZE; 86562306a36Sopenharmony_ci else *len_out = 0; 86662306a36Sopenharmony_ci return ret; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_civoid free_output_buffer(void *buffer) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci free_pages((unsigned long) buffer, 0); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ciint tap_setup_common(char *str, char *type, char **dev_name, char **mac_out, 87562306a36Sopenharmony_ci char **gate_addr) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci char *remain; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci remain = split_if_spec(str, dev_name, mac_out, gate_addr, NULL); 88062306a36Sopenharmony_ci if (remain != NULL) { 88162306a36Sopenharmony_ci printk(KERN_ERR "tap_setup_common - Extra garbage on " 88262306a36Sopenharmony_ci "specification : '%s'\n", remain); 88362306a36Sopenharmony_ci return 1; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return 0; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ciunsigned short eth_protocol(struct sk_buff *skb) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci return eth_type_trans(skb, skb->dev); 89262306a36Sopenharmony_ci} 893