Module Name:    src
Committed By:   jruoho
Date:           Wed May 12 15:59:52 UTC 2010

Modified Files:
        src/sys/dev/acpi: acpi_power.c

Log Message:
Initial pass for issues observed by dyoung@ on HP Pavilion N3270. In this
laptop there is a PNP0C0B ("ACPI fan") with the following properties:

        _PSC : Power state for D3 (alone).
        _PR0 : Power resources for D0.
        _PSx : Power state switch for D0 and D3.

Thus, it is impossible to get or set the D3 power state via power resources
alone; there is only a single PowerResource() and it is for D0.

To tackle this:

  1. Evaluate the direct _PSC control method if and only if there is no
     given _PRx. The order is important; it is known that some other
     systems implement the _PSC method (like _STA) incorrectly.

  2. If no _PRx is available (and thus no _ON or _OFF), do not error out.
     Instead, if we have AE_NOT_FOUND, continue to evaluate the power state
     switch method, _PSx, which (on this laptop) should alone suffice for
     the D0 -> D3 transition.


To generate a diff of this commit:
cvs rdiff -u -r1.11 -r1.12 src/sys/dev/acpi/acpi_power.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/acpi/acpi_power.c
diff -u src/sys/dev/acpi/acpi_power.c:1.11 src/sys/dev/acpi/acpi_power.c:1.12
--- src/sys/dev/acpi/acpi_power.c:1.11	Tue Apr 27 05:34:14 2010
+++ src/sys/dev/acpi/acpi_power.c	Wed May 12 15:59:52 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: acpi_power.c,v 1.11 2010/04/27 05:34:14 jruoho Exp $ */
+/* $NetBSD: acpi_power.c,v 1.12 2010/05/12 15:59:52 jruoho Exp $ */
 
 /*-
  * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
@@ -56,7 +56,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: acpi_power.c,v 1.11 2010/04/27 05:34:14 jruoho Exp $");
+__KERNEL_RCSID(0, "$NetBSD: acpi_power.c,v 1.12 2010/05/12 15:59:52 jruoho Exp $");
 
 #include <sys/param.h>
 #include <sys/kmem.h>
@@ -104,10 +104,7 @@
 static struct acpi_power_res	*acpi_power_res_init(ACPI_HANDLE);
 static struct acpi_power_res	*acpi_power_res_get(ACPI_HANDLE);
 
-#if 0
 static ACPI_STATUS	 acpi_power_get_direct(struct acpi_devnode *);
-#endif
-
 static ACPI_STATUS	 acpi_power_get_indirect(struct acpi_devnode *);
 static ACPI_STATUS	 acpi_power_switch(struct acpi_devnode *,
 						   int, bool);
@@ -267,9 +264,18 @@
 		goto fail;
 	}
 
+	/*
+	 * Because the _PSC control method, like _STA,
+	 * is known to be implemented incorrectly in
+	 * many systems, we first try to retrieve the
+	 * power state indirectly via power resources.
+	 */
 	rv = acpi_power_get_indirect(ad);
 
 	if (ACPI_FAILURE(rv))
+		rv = acpi_power_get_direct(ad);
+
+	if (ACPI_FAILURE(rv))
 		goto fail;
 
 	KASSERT(ad->ad_state != ACPI_STATE_ERROR);
@@ -296,11 +302,6 @@
 	return false;
 }
 
-#if 0
-/*
- * Unfortunately, comparable to _STA, there are systems
- * where the convenient _PSC is implemented incorrectly.
- */
 static ACPI_STATUS
 acpi_power_get_direct(struct acpi_devnode *ad)
 {
@@ -315,7 +316,6 @@
 
 	return rv;
 }
-#endif
 
 static ACPI_STATUS
 acpi_power_get_indirect(struct acpi_devnode *ad)
@@ -410,27 +410,25 @@
 	}
 
 	/*
-	 * We first sweep through the resources required
-	 * for the target state, turning things on and
-	 * building references. After this we dereference
-	 * the resources required for the current state,
+	 * We first sweep through the resources required for the target
+	 * state, turning things on and building references. After this
+	 * we dereference the resources required for the current state,
 	 * turning the resources off as we go.
 	 */
 	rv = acpi_power_switch(ad, state, true);
 
-	if (ACPI_FAILURE(rv))
+	if (ACPI_FAILURE(rv) && rv != AE_CTRL_CONTINUE)
 		goto fail;
 
 	rv = acpi_power_switch(ad, ad->ad_state, false);
 
-	if (ACPI_FAILURE(rv))
+	if (ACPI_FAILURE(rv) && rv != AE_CTRL_CONTINUE)
 		goto fail;
 
-	ad->ad_state = state;
-
 	/*
-	 * Last but not least, invoke the power
-	 * state switch method, if available.
+	 * Last but not least, invoke the power state switch method,
+	 * if available. Because some systems use only _PSx for the
+	 * power state transitions, we do this even if there is no _PRx.
 	 */
 	(void)snprintf(path, sizeof(path), "_PS%d", state);
 	(void)AcpiEvaluateObject(ad->ad_handle, path, NULL, NULL);
@@ -438,6 +436,8 @@
 	aprint_debug_dev(ad->ad_root, "%s turned from "
 	    "D%d to D%d\n", ad->ad_name, old, state);
 
+	ad->ad_state = state;
+
 	return true;
 
 fail:
@@ -472,13 +472,16 @@
 	pkg = acpi_power_pkg_get(ad->ad_handle, state);
 
 	if (pkg == NULL)
-		return AE_NOT_EXIST;
+		return AE_CTRL_CONTINUE;
 
 	if (on != false)
 		rv = acpi_foreach_package_object(pkg, acpi_power_res_on, ad);
 	else
 		rv = acpi_foreach_package_object(pkg, acpi_power_res_off, ad);
 
+	if (rv == AE_CTRL_CONTINUE)
+		rv = AE_ERROR;
+
 	ACPI_FREE(pkg);
 
 	return rv;

Reply via email to