Merge commit 'origin/master' into next
[pandora-kernel.git] / drivers / acpi / acpica / nsxfname.c
index 9589fea..f23593d 100644 (file)
@@ -45,6 +45,8 @@
 #include <acpi/acpi.h>
 #include "accommon.h"
 #include "acnamesp.h"
+#include "acparser.h"
+#include "amlcode.h"
 
 #define _COMPONENT          ACPI_NAMESPACE
 ACPI_MODULE_NAME("nsxfname")
@@ -358,3 +360,151 @@ acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer)
 }
 
 ACPI_EXPORT_SYMBOL(acpi_get_object_info)
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_install_method
+ *
+ * PARAMETERS:  Buffer         - An ACPI table containing one control method
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Install a control method into the namespace. If the method
+ *              name already exists in the namespace, it is overwritten. The
+ *              input buffer must contain a valid DSDT or SSDT containing a
+ *              single control method.
+ *
+ ******************************************************************************/
+acpi_status acpi_install_method(u8 *buffer)
+{
+       struct acpi_table_header *table =
+           ACPI_CAST_PTR(struct acpi_table_header, buffer);
+       u8 *aml_buffer;
+       u8 *aml_start;
+       char *path;
+       struct acpi_namespace_node *node;
+       union acpi_operand_object *method_obj;
+       struct acpi_parse_state parser_state;
+       u32 aml_length;
+       u16 opcode;
+       u8 method_flags;
+       acpi_status status;
+
+       /* Parameter validation */
+
+       if (!buffer) {
+               return AE_BAD_PARAMETER;
+       }
+
+       /* Table must be a DSDT or SSDT */
+
+       if (!ACPI_COMPARE_NAME(table->signature, ACPI_SIG_DSDT) &&
+           !ACPI_COMPARE_NAME(table->signature, ACPI_SIG_SSDT)) {
+               return AE_BAD_HEADER;
+       }
+
+       /* First AML opcode in the table must be a control method */
+
+       parser_state.aml = buffer + sizeof(struct acpi_table_header);
+       opcode = acpi_ps_peek_opcode(&parser_state);
+       if (opcode != AML_METHOD_OP) {
+               return AE_BAD_PARAMETER;
+       }
+
+       /* Extract method information from the raw AML */
+
+       parser_state.aml += acpi_ps_get_opcode_size(opcode);
+       parser_state.pkg_end = acpi_ps_get_next_package_end(&parser_state);
+       path = acpi_ps_get_next_namestring(&parser_state);
+       method_flags = *parser_state.aml++;
+       aml_start = parser_state.aml;
+       aml_length = ACPI_PTR_DIFF(parser_state.pkg_end, aml_start);
+
+       /*
+        * Allocate resources up-front. We don't want to have to delete a new
+        * node from the namespace if we cannot allocate memory.
+        */
+       aml_buffer = ACPI_ALLOCATE(aml_length);
+       if (!aml_buffer) {
+               return AE_NO_MEMORY;
+       }
+
+       method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
+       if (!method_obj) {
+               ACPI_FREE(aml_buffer);
+               return AE_NO_MEMORY;
+       }
+
+       /* Lock namespace for acpi_ns_lookup, we may be creating a new node */
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+       if (ACPI_FAILURE(status)) {
+               goto error_exit;
+       }
+
+       /* The lookup either returns an existing node or creates a new one */
+
+       status =
+           acpi_ns_lookup(NULL, path, ACPI_TYPE_METHOD, ACPI_IMODE_LOAD_PASS1,
+                          ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND,
+                          NULL, &node);
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+       if (ACPI_FAILURE(status)) {     /* ns_lookup */
+               if (status != AE_ALREADY_EXISTS) {
+                       goto error_exit;
+               }
+
+               /* Node existed previously, make sure it is a method node */
+
+               if (node->type != ACPI_TYPE_METHOD) {
+                       status = AE_TYPE;
+                       goto error_exit;
+               }
+       }
+
+       /* Copy the method AML to the local buffer */
+
+       ACPI_MEMCPY(aml_buffer, aml_start, aml_length);
+
+       /* Initialize the method object with the new method's information */
+
+       method_obj->method.aml_start = aml_buffer;
+       method_obj->method.aml_length = aml_length;
+
+       method_obj->method.param_count = (u8)
+           (method_flags & AML_METHOD_ARG_COUNT);
+
+       method_obj->method.method_flags = (u8)
+           (method_flags & ~AML_METHOD_ARG_COUNT);
+
+       if (method_flags & AML_METHOD_SERIALIZED) {
+               method_obj->method.sync_level = (u8)
+                   ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4);
+       }
+
+       /*
+        * Now that it is complete, we can attach the new method object to
+        * the method Node (detaches/deletes any existing object)
+        */
+       status = acpi_ns_attach_object(node, method_obj, ACPI_TYPE_METHOD);
+
+       /*
+        * Flag indicates AML buffer is dynamic, must be deleted later.
+        * Must be set only after attach above.
+        */
+       node->flags |= ANOBJ_ALLOCATED_BUFFER;
+
+       /* Remove local reference to the method object */
+
+       acpi_ut_remove_reference(method_obj);
+       return status;
+
+error_exit:
+
+       ACPI_FREE(aml_buffer);
+       ACPI_FREE(method_obj);
+       return status;
+}
+ACPI_EXPORT_SYMBOL(acpi_install_method)