vserver 2.0 rc7
[linux-2.6.git] / arch / um / drivers / net_kern.c
index 4c2ee09..4eeaf88 100644 (file)
@@ -19,6 +19,8 @@
 #include "linux/inetdevice.h"
 #include "linux/ctype.h"
 #include "linux/bootmem.h"
+#include "linux/ethtool.h"
+#include "asm/uaccess.h"
 #include "user_util.h"
 #include "kern_util.h"
 #include "net_kern.h"
 #include "mconsole_kern.h"
 #include "init.h"
 #include "irq_user.h"
+#include "irq_kern.h"
 
-static spinlock_t opened_lock = SPIN_LOCK_UNLOCKED;
+#define DRIVER_NAME "uml-netdev"
+
+static DEFINE_SPINLOCK(opened_lock);
 LIST_HEAD(opened);
 
 static int uml_net_rx(struct net_device *dev)
@@ -37,7 +42,8 @@ static int uml_net_rx(struct net_device *dev)
        struct sk_buff *skb;
 
        /* If we can't allocate memory, try again next round. */
-       if ((skb = dev_alloc_skb(dev->mtu)) == NULL) {
+       skb = dev_alloc_skb(dev->mtu);
+       if (skb == NULL) {
                lp->stats.rx_dropped++;
                return 0;
        }
@@ -61,14 +67,14 @@ static int uml_net_rx(struct net_device *dev)
        return pkt_len;
 }
 
-void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct net_device *dev = dev_id;
        struct uml_net_private *lp = dev->priv;
        int err;
 
        if(!netif_running(dev))
-               return;
+               return(IRQ_NONE);
 
        spin_lock(&lp->lock);
        while((err = uml_net_rx(dev)) > 0) ;
@@ -83,6 +89,7 @@ void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
  out:
        spin_unlock(&lp->lock);
+       return(IRQ_HANDLED);
 }
 
 static int uml_net_open(struct net_device *dev)
@@ -121,10 +128,12 @@ static int uml_net_open(struct net_device *dev)
        lp->tl.data = (unsigned long) &lp->user;
        netif_start_queue(dev);
 
-       spin_lock(&opened_lock);
-       list_add(&lp->list, &opened);
-       spin_unlock(&opened_lock);
-       MOD_INC_USE_COUNT;
+       /* clear buffer - it can happen that the host side of the interface
+        * is full when we get here.  In this case, new data is never queued,
+        * SIGIOs never arrive, and the net never works.
+        */
+       while((err = uml_net_rx(dev)) > 0) ;
+
  out:
        spin_unlock(&lp->lock);
        return(err);
@@ -137,14 +146,12 @@ static int uml_net_close(struct net_device *dev)
        netif_stop_queue(dev);
        spin_lock(&lp->lock);
 
+       free_irq_by_irq_and_dev(dev->irq, dev);
        free_irq(dev->irq, dev);
-       if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+       if(lp->close != NULL)
+               (*lp->close)(lp->fd, &lp->user);
        lp->fd = -1;
-       spin_lock(&opened_lock);
-       list_del(&lp->list);
-       spin_unlock(&opened_lock);
 
-       MOD_DEC_USE_COUNT;
        spin_unlock(&lp->lock);
        return 0;
 }
@@ -239,7 +246,30 @@ static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
 
 static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-       return(-EINVAL);
+       static const struct ethtool_drvinfo info = {
+               .cmd     = ETHTOOL_GDRVINFO,
+               .driver  = DRIVER_NAME,
+               .version = "42",
+       };
+       void *useraddr;
+       u32 ethcmd;
+
+       switch (cmd) {
+       case SIOCETHTOOL:
+               useraddr = ifr->ifr_data;
+               if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
+                       return -EFAULT;
+               switch (ethcmd) {
+               case ETHTOOL_GDRVINFO:
+                       if (copy_to_user(useraddr, &info, sizeof(info)))
+                               return -EFAULT;
+                       return 0;
+               default:
+                       return -EOPNOTSUPP;
+               }
+       default:
+               return -EINVAL;
+       }
 }
 
 void uml_net_user_timer_expire(unsigned long _conn)
@@ -252,47 +282,22 @@ void uml_net_user_timer_expire(unsigned long _conn)
 #endif
 }
 
-/*
- * default do nothing hard header packet routines for struct net_device init.
- * real ethernet transports will overwrite with real routines.
- */
-static int uml_net_hard_header(struct sk_buff *skb, struct net_device *dev,
-                 unsigned short type, void *daddr, void *saddr, unsigned len)
-{
-       return(0); /* no change */
-}
-
-static int uml_net_rebuild_header(struct sk_buff *skb)
-{
-       return(0); /* ignore */ 
-}
-
-static int uml_net_header_cache(struct neighbour *neigh, struct hh_cache *hh)
-{
-       return(-1); /* fail */
-}
-
-static void uml_net_header_cache_update(struct hh_cache *hh,
-                 struct net_device *dev, unsigned char * haddr)
-{
-       /* ignore */
-}
-
-static int uml_net_header_parse(struct sk_buff *skb, unsigned char *haddr)
-{
-       return(0); /* nothing */
-}
-
-static spinlock_t devices_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(devices_lock);
 static struct list_head devices = LIST_HEAD_INIT(devices);
 
+static struct device_driver uml_net_driver = {
+       .name  = DRIVER_NAME,
+       .bus   = &platform_bus_type,
+};
+static int driver_registered;
+
 static int eth_configure(int n, void *init, char *mac,
                         struct transport *transport)
 {
        struct uml_net *device;
        struct net_device *dev;
        struct uml_net_private *lp;
-       int err, size;
+       int save, err, size;
 
        size = transport->private_size + sizeof(struct uml_net_private) + 
                sizeof(((struct uml_net_private *) 0)->user);
@@ -327,6 +332,16 @@ static int eth_configure(int n, void *init, char *mac,
                return 1;
        }
 
+       /* sysfs register */
+       if (!driver_registered) {
+               driver_register(&uml_net_driver);
+               driver_registered = 1;
+       }
+       device->pdev.id = n;
+       device->pdev.name = DRIVER_NAME;
+       platform_device_register(&device->pdev);
+       SET_NETDEV_DEV(dev,&device->pdev.dev);
+
        /* If this name ends up conflicting with an existing registered
         * netdevice, that is OK, register_netdev{,ice}() will notice this
         * and fail.
@@ -334,12 +349,6 @@ static int eth_configure(int n, void *init, char *mac,
        snprintf(dev->name, sizeof(dev->name), "eth%d", n);
        device->dev = dev;
 
-        dev->hard_header = uml_net_hard_header;
-        dev->rebuild_header = uml_net_rebuild_header;
-        dev->hard_header_cache = uml_net_header_cache;
-        dev->header_cache_update= uml_net_header_cache_update;
-        dev->hard_header_parse = uml_net_header_parse;
-
        (*transport->kern->init)(dev, init);
 
        dev->mtu = transport->user->max_packet;
@@ -366,23 +375,31 @@ static int eth_configure(int n, void *init, char *mac,
        }
        lp = dev->priv;
 
-       INIT_LIST_HEAD(&lp->list);
-       spin_lock_init(&lp->lock);
-       lp->dev = dev;
-       lp->fd = -1;
-       lp->mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 };
-       lp->have_mac = device->have_mac;
-       lp->protocol = transport->kern->protocol;
-       lp->open = transport->user->open;
-       lp->close = transport->user->close;
-       lp->remove = transport->user->remove;
-       lp->read = transport->kern->read;
-       lp->write = transport->kern->write;
-       lp->add_address = transport->user->add_address;
-       lp->delete_address = transport->user->delete_address;
-       lp->set_mtu = transport->user->set_mtu;
+       /* lp.user is the first four bytes of the transport data, which
+        * has already been initialized.  This structure assignment will
+        * overwrite that, so we make sure that .user gets overwritten with
+        * what it already has.
+        */
+       save = lp->user[0];
+       *lp = ((struct uml_net_private)
+               { .list                 = LIST_HEAD_INIT(lp->list),
+                 .dev                  = dev,
+                 .fd                   = -1,
+                 .mac                  = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0},
+                 .have_mac             = device->have_mac,
+                 .protocol             = transport->kern->protocol,
+                 .open                 = transport->user->open,
+                 .close                = transport->user->close,
+                 .remove               = transport->user->remove,
+                 .read                 = transport->kern->read,
+                 .write                = transport->kern->write,
+                 .add_address          = transport->user->add_address,
+                 .delete_address       = transport->user->delete_address,
+                 .set_mtu              = transport->user->set_mtu,
+                 .user                 = { save } });
 
        init_timer(&lp->tl);
+       spin_lock_init(&lp->lock);
        lp->tl.function = uml_net_user_timer_expire;
        if (lp->have_mac)
                memcpy(lp->mac, device->mac, sizeof(lp->mac));
@@ -392,6 +409,11 @@ static int eth_configure(int n, void *init, char *mac,
 
        if (device->have_mac)
                set_ether_mac(dev, device->mac);
+
+       spin_lock(&opened_lock);
+       list_add(&lp->list, &opened);
+       spin_unlock(&opened_lock);
+
        return(0);
 }
 
@@ -555,6 +577,7 @@ __uml_help(eth_setup,
 "    Configure a network device.\n\n"
 );
 
+#if 0
 static int eth_init(void)
 {
        struct list_head *ele, *next;
@@ -569,8 +592,8 @@ static int eth_init(void)
        
        return(1);
 }
-
 __initcall(eth_init);
+#endif
 
 static int net_config(char *str)
 {
@@ -611,9 +634,11 @@ static int net_remove(char *str)
        if(lp->fd > 0) return(-1);
        if(lp->remove != NULL) (*lp->remove)(&lp->user);
        unregister_netdev(dev);
+       platform_device_unregister(&device->pdev);
 
        list_del(&device->list);
-       free_netdev(device);
+       kfree(device);
+       free_netdev(dev);
        return(0);
 }
 
@@ -699,11 +724,12 @@ __initcall(uml_net_init);
 static void close_devices(void)
 {
        struct list_head *ele;
-       struct uml_net_private *lp;     
+       struct uml_net_private *lp;
 
        list_for_each(ele, &opened){
                lp = list_entry(ele, struct uml_net_private, list);
-               if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+               if((lp->close != NULL) && (lp->fd >= 0))
+                       (*lp->close)(lp->fd, &lp->user);
                if(lp->remove != NULL) (*lp->remove)(&lp->user);
        }
 }