Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / pnp / pnpbios / core.c
index 967ecb2..551f58e 100644 (file)
@@ -12,7 +12,7 @@
  * Minor reorganizations by David Hinds <dahinds@users.sourceforge.net>
  * Further modifications (C) 2001, 2002 by:
  *   Alan Cox <alan@redhat.com>
- *   Thomas Hood <jdthood@mail.com>
+ *   Thomas Hood
  *   Brian Gerst <bgerst@didntduck.org>
  *
  * Ported to the PnP Layer and several additional improvements (C) 2002
 #include <linux/pnpbios.h>
 #include <linux/device.h>
 #include <linux/pnp.h>
-#include <asm/page.h>
-#include <asm/system.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
-#include <asm/desc.h>
 #include <linux/slab.h>
-#include <linux/kmod.h>
 #include <linux/completion.h>
 #include <linux/spinlock.h>
+#include <linux/dmi.h>
+#include <linux/delay.h>
+#include <linux/acpi.h>
+
+#include <asm/page.h>
+#include <asm/desc.h>
 #include <asm/system.h>
 #include <asm/byteorder.h>
 
@@ -83,16 +85,6 @@ int pnp_bios_present(void)
 
 struct pnp_dev_node_info node_info;
 
-void *pnpbios_kmalloc(size_t size, int f)
-{
-       void *p = kmalloc( size, f );
-       if ( p == NULL )
-               printk(KERN_ERR "PnPBIOS: kmalloc() failed\n");
-       else
-               memset(p, 0, size);
-       return p;
-}
-
 /*
  *
  * DOCKING FUNCTIONS
@@ -113,23 +105,22 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
        char *argv [3], **envp, *buf, *scratch;
        int i = 0, value;
 
-       if (!hotplug_path [0])
-               return -ENOENT;
        if (!current->fs->root) {
                return -EAGAIN;
        }
-       if (!(envp = (char **) pnpbios_kmalloc (20 * sizeof (char *), GFP_KERNEL))) {
+       if (!(envp = (char **) kcalloc (20, sizeof (char *), GFP_KERNEL))) {
                return -ENOMEM;
        }
-       if (!(buf = pnpbios_kmalloc (256, GFP_KERNEL))) {
+       if (!(buf = kcalloc (1, 256, GFP_KERNEL))) {
                kfree (envp);
                return -ENOMEM;
        }
 
-       /* only one standardized param to hotplug command: type */
-       argv [0] = hotplug_path;
+       /* FIXME: if there are actual users of this, it should be integrated into
+        * the driver core and use the usual infrastructure like sysfs and uevents */
+       argv [0] = "/sbin/pnpbios";
        argv [1] = "dock";
-       argv [2] = 0;
+       argv [2] = NULL;
 
        /* minimal command environment */
        envp [i++] = "HOME=/";
@@ -152,7 +143,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
        envp [i++] = scratch;
        scratch += sprintf (scratch, "DOCK=%x/%x/%x",
                info->location_id, info->serial, info->capabilities);
-       envp[i] = 0;
+       envp[i] = NULL;
        
        value = call_usermodehelper (argv [0], argv, envp, 0);
        kfree (buf);
@@ -176,10 +167,13 @@ static int pnp_dock_thread(void * unused)
                /*
                 * Poll every 2 seconds
                 */
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ*2);
-               if(signal_pending(current))
+               msleep_interruptible(2000);
+
+               if(signal_pending(current)) {
+                       if (try_to_freeze())
+                               continue;
                        break;
+               }
 
                status = pnp_bios_dock_station_info(&now);
 
@@ -225,7 +219,7 @@ static int pnpbios_get_resources(struct pnp_dev * dev, struct pnp_resource_table
        if(!pnpbios_is_dynamic(dev))
                return -EPERM;
 
-       node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL);
+       node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
        if (!node)
                return -1;
        if (pnp_bios_get_dev_node(&nodenum, (char )PNPMODE_DYNAMIC, node)) {
@@ -248,11 +242,13 @@ static int pnpbios_set_resources(struct pnp_dev * dev, struct pnp_resource_table
        if (!pnpbios_is_dynamic(dev))
                return -EPERM;
 
-       node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL);
+       node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
        if (!node)
                return -1;
-       if (pnp_bios_get_dev_node(&nodenum, (char )PNPMODE_DYNAMIC, node))
+       if (pnp_bios_get_dev_node(&nodenum, (char )PNPMODE_DYNAMIC, node)) {
+               kfree(node);
                return -ENODEV;
+       }
        if(pnpbios_write_resources_to_node(res, node)<0) {
                kfree(node);
                return -1;
@@ -297,7 +293,7 @@ static int pnpbios_disable_resources(struct pnp_dev *dev)
        if(dev->flags & PNPBIOS_NO_DISABLE || !pnpbios_is_dynamic(dev))
                return -EPERM;
 
-       node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL);
+       node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
        if (!node)
                return -ENOMEM;
 
@@ -339,7 +335,7 @@ static int insert_device(struct pnp_dev *dev, struct pnp_bios_node * node)
        }
 
        /* set the initial values for the PnP device */
-       dev_id = pnpbios_kmalloc(sizeof(struct pnp_id), GFP_KERNEL);
+       dev_id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL);
        if (!dev_id)
                return -1;
        pnpid32_to_pnpid(node->eisa_id,id);
@@ -350,7 +346,7 @@ static int insert_device(struct pnp_dev *dev, struct pnp_bios_node * node)
        dev->flags = node->flags;
        if (!(dev->flags & PNPBIOS_NO_CONFIG))
                dev->capabilities |= PNP_CONFIGURABLE;
-       if (!(dev->flags & PNPBIOS_NO_DISABLE))
+       if (!(dev->flags & PNPBIOS_NO_DISABLE) && pnpbios_is_dynamic(dev))
                dev->capabilities |= PNP_DISABLE;
        dev->capabilities |= PNP_READ;
        if (pnpbios_is_dynamic(dev))
@@ -377,7 +373,7 @@ static void __init build_devlist(void)
        struct pnp_bios_node *node;
        struct pnp_dev *dev;
 
-       node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL);
+       node = kcalloc(1, node_info.max_node_size, GFP_KERNEL);
        if (!node)
                return;
 
@@ -394,7 +390,7 @@ static void __init build_devlist(void)
                                break;
                }
                nodes_got++;
-               dev =  pnpbios_kmalloc(sizeof (struct pnp_dev), GFP_KERNEL);
+               dev =  kcalloc(1, sizeof (struct pnp_dev), GFP_KERNEL);
                if (!dev)
                        break;
                if(insert_device(dev,node)<0)
@@ -450,7 +446,7 @@ __setup("pnpbios=", pnpbios_setup);
 /* PnP BIOS signature: "$PnP" */
 #define PNP_SIGNATURE   (('$' << 0) + ('P' << 8) + ('n' << 16) + ('P' << 24))
 
-int __init pnpbios_probe_system(void)
+static int __init pnpbios_probe_system(void)
 {
        union pnp_bios_install_struct *check;
        u8 sum;
@@ -498,14 +494,51 @@ int __init pnpbios_probe_system(void)
        return 0;
 }
 
-int __init pnpbios_init(void)
+static int __init exploding_pnp_bios(struct dmi_system_id *d)
+{
+       printk(KERN_WARNING "%s detected. Disabling PnPBIOS\n", d->ident);
+       return 0;
+}
+
+static struct dmi_system_id pnpbios_dmi_table[] __initdata = {
+       {       /* PnPBIOS GPF on boot */
+               .callback = exploding_pnp_bios,
+               .ident = "Higraded P14H",
+               .matches = {
+                       DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
+                       DMI_MATCH(DMI_BIOS_VERSION, "07.00T"),
+                       DMI_MATCH(DMI_SYS_VENDOR, "Higraded"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "P14H"),
+               },
+       },
+       {       /* PnPBIOS GPF on boot */
+               .callback = exploding_pnp_bios,
+               .ident = "ASUS P4P800",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
+                       DMI_MATCH(DMI_BOARD_NAME, "P4P800"),
+               },
+       },
+       { }
+};
+
+static int __init pnpbios_init(void)
 {
        int ret;
-       if(pnpbios_disabled || (dmi_broken & BROKEN_PNP_BIOS)) {
+
+       if (pnpbios_disabled || dmi_check_system(pnpbios_dmi_table)) {
                printk(KERN_INFO "PnPBIOS: Disabled\n");
                return -ENODEV;
        }
 
+#ifdef CONFIG_PNPACPI
+       if (!acpi_disabled && !pnpacpi_disabled) {
+               pnpbios_disabled = 1;
+               printk(KERN_INFO "PnPBIOS: Disabled by ACPI PNP\n");
+               return -ENODEV;
+       }
+#endif /* CONFIG_ACPI */
+
        /* scan the system for pnpbios support */
        if (!pnpbios_probe_system())
                return -ENODEV;
@@ -542,6 +575,8 @@ subsys_initcall(pnpbios_init);
 
 static int __init pnpbios_thread_init(void)
 {
+       if (pnpbios_disabled)
+               return 0;
 #ifdef CONFIG_HOTPLUG
        init_completion(&unload_sem);
        if (kernel_thread(pnp_dock_thread, NULL, CLONE_KERNEL) > 0)