Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/shaggy...
[pandora-kernel.git] / arch / um / drivers / net_kern.c
index 84c73a3..286bc0b 100644 (file)
@@ -5,7 +5,6 @@
  * Licensed under the GPL.
  */
 
-#include "linux/config.h"
 #include "linux/kernel.h"
 #include "linux/netdevice.h"
 #include "linux/rtnetlink.h"
 #include "irq_user.h"
 #include "irq_kern.h"
 
+static inline void set_ether_mac(struct net_device *dev, unsigned char *addr)
+{
+       memcpy(dev->dev_addr, addr, ETH_ALEN);
+}
+
 #define DRIVER_NAME "uml-netdev"
 
 static DEFINE_SPINLOCK(opened_lock);
-LIST_HEAD(opened);
+static LIST_HEAD(opened);
 
 static int uml_net_rx(struct net_device *dev)
 {
@@ -68,7 +72,12 @@ static int uml_net_rx(struct net_device *dev)
        return pkt_len;
 }
 
-irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void uml_dev_close(void* dev)
+{
+       dev_close( (struct net_device *) dev);
+}
+
+irqreturn_t uml_net_interrupt(int irq, void *dev_id)
 {
        struct net_device *dev = dev_id;
        struct uml_net_private *lp = dev->priv;
@@ -80,15 +89,22 @@ irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        spin_lock(&lp->lock);
        while((err = uml_net_rx(dev)) > 0) ;
        if(err < 0) {
+               DECLARE_WORK(close_work, uml_dev_close, dev);
                printk(KERN_ERR 
                       "Device '%s' read returned %d, shutting it down\n", 
                       dev->name, err);
-               dev_close(dev);
+               /* dev_close can't be called in interrupt context, and takes
+                * again lp->lock.
+                * And dev_close() can be safely called multiple times on the
+                * same device, since it tests for (dev->flags & IFF_UP). So
+                * there's no harm in delaying the device shutdown. */
+               schedule_work(&close_work);
+#error this is not permitted - close_work will go out of scope
                goto out;
        }
        reactivate_fd(lp->fd, UM_ETH_IRQ);
 
- out:
+out:
        spin_unlock(&lp->lock);
        return(IRQ_HANDLED);
 }
@@ -98,18 +114,11 @@ static int uml_net_open(struct net_device *dev)
        struct uml_net_private *lp = dev->priv;
        int err;
 
-       spin_lock(&lp->lock);
-
        if(lp->fd >= 0){
                err = -ENXIO;
                goto out;
        }
 
-       if(!lp->have_mac){
-               dev_ip_addr(dev, &lp->mac[2]);
-               set_ether_mac(dev, lp->mac);
-       }
-
        lp->fd = (*lp->open)(&lp->user);
        if(lp->fd < 0){
                err = lp->fd;
@@ -117,12 +126,11 @@ static int uml_net_open(struct net_device *dev)
        }
 
        err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,
-                            SA_INTERRUPT | SA_SHIRQ, dev->name, dev);
+                            IRQF_DISABLED | IRQF_SHARED, dev->name, dev);
        if(err != 0){
                printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
-               if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
-               lp->fd = -1;
                err = -ENETUNREACH;
+               goto out_close;
        }
 
        lp->tl.data = (unsigned long) &lp->user;
@@ -134,9 +142,16 @@ static int uml_net_open(struct net_device *dev)
         */
        while((err = uml_net_rx(dev)) > 0) ;
 
- out:
-       spin_unlock(&lp->lock);
-       return(err);
+       spin_lock(&opened_lock);
+       list_add(&lp->list, &opened);
+       spin_unlock(&opened_lock);
+
+       return 0;
+out_close:
+       if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+       lp->fd = -1;
+out:
+       return err;
 }
 
 static int uml_net_close(struct net_device *dev)
@@ -144,14 +159,16 @@ static int uml_net_close(struct net_device *dev)
        struct uml_net_private *lp = dev->priv;
        
        netif_stop_queue(dev);
-       spin_lock(&lp->lock);
 
        free_irq(dev->irq, dev);
        if(lp->close != NULL)
                (*lp->close)(lp->fd, &lp->user);
        lp->fd = -1;
 
-       spin_unlock(&lp->lock);
+       spin_lock(&opened_lock);
+       list_del(&lp->list);
+       spin_unlock(&opened_lock);
+
        return 0;
 }
 
@@ -216,9 +233,9 @@ static int uml_net_set_mac(struct net_device *dev, void *addr)
        struct uml_net_private *lp = dev->priv;
        struct sockaddr *hwaddr = addr;
 
-       spin_lock(&lp->lock);
-       memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);
-       spin_unlock(&lp->lock);
+       spin_lock_irq(&lp->lock);
+       set_ether_mac(dev, hwaddr->sa_data);
+       spin_unlock_irq(&lp->lock);
 
        return(0);
 }
@@ -228,7 +245,7 @@ static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
        struct uml_net_private *lp = dev->priv;
        int err = 0;
 
-       spin_lock(&lp->lock);
+       spin_lock_irq(&lp->lock);
 
        new_mtu = (*lp->set_mtu)(new_mtu, &lp->user);
        if(new_mtu < 0){
@@ -239,7 +256,7 @@ static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
        dev->mtu = new_mtu;
 
  out:
-       spin_unlock(&lp->lock);
+       spin_unlock_irq(&lp->lock);
        return err;
 }
 
@@ -265,8 +282,39 @@ void uml_net_user_timer_expire(unsigned long _conn)
 #endif
 }
 
+static void setup_etheraddr(char *str, unsigned char *addr)
+{
+       char *end;
+       int i;
+
+       if(str == NULL)
+               goto random;
+
+       for(i=0;i<6;i++){
+               addr[i] = simple_strtoul(str, &end, 16);
+               if((end == str) ||
+                  ((*end != ':') && (*end != ',') && (*end != '\0'))){
+                       printk(KERN_ERR
+                              "setup_etheraddr: failed to parse '%s' "
+                              "as an ethernet address\n", str);
+                       goto random;
+               }
+               str = end + 1;
+       }
+       if(addr[0] & 1){
+               printk(KERN_ERR
+                      "Attempt to assign a broadcast ethernet address to a "
+                      "device disallowed\n");
+               goto random;
+       }
+       return;
+
+random:
+       random_ether_addr(addr);
+}
+
 static DEFINE_SPINLOCK(devices_lock);
-static struct list_head devices = LIST_HEAD_INIT(devices);
+static LIST_HEAD(devices);
 
 static struct platform_driver uml_net_driver = {
        .driver = {
@@ -300,15 +348,13 @@ static int eth_configure(int n, void *init, char *mac,
        list_add(&device->list, &devices);
        spin_unlock(&devices_lock);
 
-       if (setup_etheraddr(mac, device->mac))
-               device->have_mac = 1;
+       setup_etheraddr(mac, device->mac);
 
        printk(KERN_INFO "Netdevice %d ", n);
-       if (device->have_mac)
-               printk("(%02x:%02x:%02x:%02x:%02x:%02x) ",
-                      device->mac[0], device->mac[1],
-                      device->mac[2], device->mac[3],
-                      device->mac[4], device->mac[5]);
+       printk("(%02x:%02x:%02x:%02x:%02x:%02x) ",
+              device->mac[0], device->mac[1],
+              device->mac[2], device->mac[3],
+              device->mac[4], device->mac[5]);
        printk(": ");
        dev = alloc_etherdev(size);
        if (dev == NULL) {
@@ -316,6 +362,11 @@ static int eth_configure(int n, void *init, char *mac,
                return 1;
        }
 
+       lp = dev->priv;
+       /* This points to the transport private data. It's still clear, but we
+        * must memset it to 0 *now*. Let's help the drivers. */
+       memset(lp, 0, size);
+
        /* sysfs register */
        if (!driver_registered) {
                platform_driver_register(&uml_net_driver);
@@ -357,7 +408,6 @@ static int eth_configure(int n, void *init, char *mac,
                free_netdev(dev);
                return 1;
        }
-       lp = dev->priv;
 
        /* lp.user is the first four bytes of the transport data, which
         * has already been initialized.  This structure assignment will
@@ -370,7 +420,6 @@ static int eth_configure(int n, void *init, char *mac,
                  .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,
@@ -385,20 +434,14 @@ static int eth_configure(int n, void *init, char *mac,
        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));
+       memcpy(lp->mac, device->mac, sizeof(lp->mac));
 
        if (transport->user->init) 
                (*transport->user->init)(&lp->user, dev);
 
-       if (device->have_mac)
-               set_ether_mac(dev, device->mac);
+       set_ether_mac(dev, device->mac);
 
-       spin_lock(&opened_lock);
-       list_add(&lp->list, &opened);
-       spin_unlock(&opened_lock);
-
-       return(0);
+       return 0;
 }
 
 static struct uml_net *find_device(int n)
@@ -539,12 +582,13 @@ static int eth_setup(char *str)
        int n, err;
 
        err = eth_parse(str, &n, &str);
-       if(err) return(1);
+       if(err)
+               return 1;
 
-       new = alloc_bootmem(sizeof(new));
+       new = alloc_bootmem(sizeof(*new));
        if (new == NULL){
                printk("eth_init : alloc_bootmem failed\n");
-               return(1);
+               return 1;
        }
 
        INIT_LIST_HEAD(&new->list);
@@ -552,7 +596,7 @@ static int eth_setup(char *str)
        new->init = str;
 
        list_add_tail(&new->list, &eth_cmd_line);
-       return(1);
+       return 1;
 }
 
 __setup("eth", eth_setup);
@@ -586,7 +630,7 @@ static int net_config(char *str)
        err = eth_parse(str, &n, &str);
        if(err) return(err);
 
-       str = uml_strdup(str);
+       str = kstrdup(str, GFP_KERNEL);
        if(str == NULL){
                printk(KERN_ERR "net_config failed to strdup string\n");
                return(-1);
@@ -715,6 +759,7 @@ static void close_devices(void)
 
        list_for_each(ele, &opened){
                lp = list_entry(ele, struct uml_net_private, list);
+               free_irq(lp->dev->irq, lp->dev);
                if((lp->close != NULL) && (lp->fd >= 0))
                        (*lp->close)(lp->fd, &lp->user);
                if(lp->remove != NULL) (*lp->remove)(&lp->user);
@@ -723,54 +768,6 @@ static void close_devices(void)
 
 __uml_exitcall(close_devices);
 
-int setup_etheraddr(char *str, unsigned char *addr)
-{
-       char *end;
-       int i;
-
-       if(str == NULL)
-               return(0);
-       for(i=0;i<6;i++){
-               addr[i] = simple_strtoul(str, &end, 16);
-               if((end == str) ||
-                  ((*end != ':') && (*end != ',') && (*end != '\0'))){
-                       printk(KERN_ERR 
-                              "setup_etheraddr: failed to parse '%s' "
-                              "as an ethernet address\n", str);
-                       return(0);
-               }
-               str = end + 1;
-       }
-       if(addr[0] & 1){
-               printk(KERN_ERR 
-                      "Attempt to assign a broadcast ethernet address to a "
-                      "device disallowed\n");
-               return(0);
-       }
-       return(1);
-}
-
-void dev_ip_addr(void *d, unsigned char *bin_buf)
-{
-       struct net_device *dev = d;
-       struct in_device *ip = dev->ip_ptr;
-       struct in_ifaddr *in;
-
-       if((ip == NULL) || ((in = ip->ifa_list) == NULL)){
-               printk(KERN_WARNING "dev_ip_addr - device not assigned an "
-                      "IP address\n");
-               return;
-       }
-       memcpy(bin_buf, &in->ifa_address, sizeof(in->ifa_address));
-}
-
-void set_ether_mac(void *d, unsigned char *addr)
-{
-       struct net_device *dev = d;
-
-       memcpy(dev->dev_addr, addr, ETH_ALEN);  
-}
-
 struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra)
 {
        if((skb != NULL) && (skb_tailroom(skb) < extra)){
@@ -808,7 +805,7 @@ int dev_netmask(void *d, void *m)
        struct net_device *dev = d;
        struct in_device *ip = dev->ip_ptr;
        struct in_ifaddr *in;
-       __u32 *mask_out = m;
+       __be32 *mask_out = m;
 
        if(ip == NULL) 
                return(1);