/******************************************************************************
 *
 * Module Name: bm.c
 *   $Revision: 42 $
 *
 *****************************************************************************/

/*
 *  Copyright (C) 2000, 2001 Andrew Grover
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <acpi.h>
#include "bm.h"


#define _COMPONENT		ACPI_BUS
	MODULE_NAME		("bm")


/****************************************************************************
 *                                  Globals
 ****************************************************************************/

extern FADT_DESCRIPTOR_REV2	acpi_fadt;
/* TBD: Make dynamically sizeable. */
BM_NODE_LIST			node_list;


/****************************************************************************
 *                            Internal Functions
 ****************************************************************************/

/*****************************************************************************
 * 
 * FUNCTION:    bm_print_object
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

void
bm_print_object (
	ACPI_HANDLE		acpi_handle)
{
	ACPI_BUFFER		buffer;
	ACPI_HANDLE		parent;
	ACPI_OBJECT_TYPE	type;

	buffer.length = 256;
	buffer.pointer = acpi_os_callocate(buffer.length);
	if (!buffer.pointer) {
		return;
	}

	acpi_get_name(acpi_handle, ACPI_FULL_PATHNAME, &buffer);
	acpi_get_parent(acpi_handle, &parent);
	acpi_get_type(acpi_handle, &type);

	/*
	 * TBD: Hack to get around scope identification problem.
	 */
	if (type == ACPI_TYPE_ANY) {
		if (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_ANY, 
			acpi_handle, 0, NULL))) {
			type = INTERNAL_TYPE_SCOPE;
		}
	}
    
	switch (type)
	{
	case INTERNAL_TYPE_SCOPE:
		acpi_os_printf("SCOPE: ");
		break;
	case ACPI_TYPE_INTEGER:
		acpi_os_printf("SIMPLE (number): ");
		break;
	case ACPI_TYPE_STRING:
		acpi_os_printf("SIMPLE (string): ");
		break;
	case ACPI_TYPE_BUFFER:
		acpi_os_printf("SIMPLE (buffer): ");
		break;
	case ACPI_TYPE_PACKAGE:
		acpi_os_printf("SIMPLE (package): ");
		break;
	case ACPI_TYPE_FIELD_UNIT:
		acpi_os_printf("FIELD UNIT: ");
		break;
	case ACPI_TYPE_DEVICE:
		acpi_os_printf("DEVICE: ");
		break;
	case ACPI_TYPE_EVENT:
		acpi_os_printf("EVENT: ");
		break;
	case ACPI_TYPE_METHOD:
		acpi_os_printf("CONTROL METHOD: ");
		break;
	case ACPI_TYPE_MUTEX:
		acpi_os_printf("MUTEX: ");
		break;
	case ACPI_TYPE_REGION:
		acpi_os_printf("OPERATION REGION: ");
		break;
	case ACPI_TYPE_POWER:
		acpi_os_printf("POWER RESOURCE: ");
		break;
	case ACPI_TYPE_PROCESSOR:
		acpi_os_printf("PROCESSOR: ");
		break;
	case ACPI_TYPE_THERMAL:
		acpi_os_printf("THERMAL ZONE: ");
		break;
	case ACPI_TYPE_BUFFER_FIELD:
		acpi_os_printf("BUFFER FIELD: ");
		break;
	case ACPI_TYPE_DDB_HANDLE:
		acpi_os_printf("DDB HANDLE: ");
		break;
	default:
		acpi_os_printf("OTHER (%d): ", type);
		break;
	}

	acpi_os_printf("Object[%p][%s] parent[%p].\n", acpi_handle, (char*)buffer.pointer, parent);

	acpi_os_free(buffer.pointer);
}


/****************************************************************************
 *
 * FUNCTION:    bm_print_node
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

void
bm_print_node (
	BM_NODE			*node,
	u32                     flags)
{
#ifdef ACPI_DEBUG
	ACPI_BUFFER             buffer;
	BM_DEVICE		*device = NULL;
	char                    *type_string = NULL;

	if (!node) {
		return;
	}

	device = &(node->device);

	if (flags & BM_PRINT_PRESENT) {
		if (!BM_DEVICE_PRESENT(device)) {
			return;
		}
	}

	buffer.length = 256;
	buffer.pointer = acpi_os_callocate(buffer.length);
	if (!buffer.pointer) {
		return;
	}

	acpi_get_name(device->acpi_handle, ACPI_FULL_PATHNAME, &buffer);

	switch(device->id.type) {
	case BM_TYPE_SYSTEM:
		type_string = " System";
		break;
	case BM_TYPE_SCOPE: 
		type_string = "  Scope";
		break;
	case BM_TYPE_PROCESSOR:
		type_string = "   Proc";
		break;
	case BM_TYPE_THERMAL_ZONE:
		type_string = "Thermal";
		break;
	case BM_TYPE_POWER_RESOURCE:
		type_string = "  Power";
		break;
	case BM_TYPE_FIXED_BUTTON:
		type_string = " Button";
		break;
	case BM_TYPE_DEVICE:
		type_string = " Device";
		break;
	default:
		type_string = "Unknown";
		break;
	}

	if (!(flags & BM_PRINT_GROUP)) {
		DEBUG_PRINT_RAW(ACPI_INFO, ("+-------------------------------------------------------------------------------\n"));
	}

	DEBUG_PRINT_RAW(ACPI_INFO, ("| %s[%02x]:[%p] flags[%02x] hid[%s] %s\n", type_string, device->handle, device->acpi_handle, device->flags, (device->id.hid[0] ? device->id.hid : "       "), buffer.pointer));

	if (flags & BM_PRINT_IDENTIFICATION) {
		DEBUG_PRINT_RAW(ACPI_INFO, ("|   identification: uid[%s] adr[%08x]\n", device->id.uid, device->id.adr));
	}

	if (flags & BM_PRINT_LINKAGE) {
		DEBUG_PRINT_RAW(ACPI_INFO, ("|   linkage: this[%p] parent[%p] next[%p]\n", node, node->parent, node->next));
		DEBUG_PRINT_RAW(ACPI_INFO, ("|     scope.head[%p] scope.tail[%p]\n", node->scope.head, node->scope.tail));
	}

	if (flags & BM_PRINT_POWER) {
		DEBUG_PRINT_RAW(ACPI_INFO, ("|   power: state[D%d] flags[%08x]\n", device->power.state, device->power.flags));
		DEBUG_PRINT_RAW(ACPI_INFO, ("|     S0[%02x] S1[%02x] S2[%02x] S3[%02x] S4[%02x] S5[%02x]\n", device->power.dx_supported[0], device->power.dx_supported[1], device->power.dx_supported[2], device->power.dx_supported[3], device->power.dx_supported[4], device->power.dx_supported[5]));
	}

	if (!(flags & BM_PRINT_GROUP)) {
		DEBUG_PRINT_RAW(ACPI_INFO, ("+-------------------------------------------------------------------------------\n"));
	}

	acpi_os_free(buffer.pointer);
#endif /*ACPI_DEBUG*/

	return;
}


/****************************************************************************
 *
 * FUNCTION:    bm_print_hierarchy
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

void
bm_print_hierarchy (void)
{
#ifdef ACPI_DEBUG
	u32			i = 0;

	FUNCTION_TRACE("bm_print_hierarchy");

	DEBUG_PRINT_RAW(ACPI_INFO, ("+------------------------------------------------------------\n"));

	for (i = 0; i < node_list.count; i++) {
		bm_print_node(node_list.nodes[i], BM_PRINT_GROUP | BM_PRINT_PRESENT);
	}

	DEBUG_PRINT_RAW(ACPI_INFO, ("+------------------------------------------------------------\n"));
#endif /*ACPI_DEBUG*/

	return_VOID;
}


/****************************************************************************
 *
 * FUNCTION:    bm_get_status
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_get_status (
	BM_DEVICE		*device)
{
	ACPI_STATUS             status = AE_OK;

	if (!device) {
		return AE_BAD_PARAMETER;
	}

	device->status = BM_STATUS_UNKNOWN;

	/*
	 * Dynamic Status?
	 * ---------------
	 * If _STA isn't present we just return the default status.
	 */
	if (!(device->flags & BM_FLAGS_DYNAMIC_STATUS)) {
		device->status = BM_STATUS_DEFAULT;
		return AE_OK;
	}

	/*
	 * Evaluate _STA:
	 * --------------
	 */
	status = bm_evaluate_simple_integer(device->acpi_handle, "_STA", 
		&(device->status));

	return status;
}


/****************************************************************************
 *
 * FUNCTION:    bm_get_identification
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_get_identification (
	BM_DEVICE		*device)
{
	ACPI_STATUS             status = AE_OK;
	ACPI_DEVICE_INFO        info;

	if (!device) {
		return AE_BAD_PARAMETER;
	}

	if (!(device->flags & BM_FLAGS_IDENTIFIABLE)) {
		return AE_OK;
	}

	device->id.uid[0] = BM_UID_UNKNOWN;
	device->id.hid[0] = BM_HID_UNKNOWN;
	device->id.adr = BM_ADDRESS_UNKNOWN;

	/*
	 * Get Object Info:
	 * ----------------
	 * Evalute _UID, _HID, and _ADR...
	 */
	status = acpi_get_object_info(device->acpi_handle, &info);
	if (ACPI_FAILURE(status)) {
		return status;
	}

	if (info.valid & ACPI_VALID_UID) {
		MEMCPY((void*)device->id.uid, (void*)info.unique_id, 
			sizeof(BM_DEVICE_UID));
	}

	if (info.valid & ACPI_VALID_HID) {
		MEMCPY((void*)device->id.hid, (void*)info.hardware_id, 
			sizeof(BM_DEVICE_HID));
	}

	if (info.valid & ACPI_VALID_ADR) {
		device->id.adr = info.address;
	}

	return status;
}


/****************************************************************************
 *
 * FUNCTION:    bm_get_flags
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_get_flags (
	BM_DEVICE		*device)
{
	ACPI_HANDLE             acpi_handle = NULL;

	if (!device) {
		return AE_BAD_PARAMETER;
	}

	device->flags = BM_FLAGS_UNKNOWN;

	switch (device->id.type) {

	case BM_TYPE_DEVICE:

		/*
		 * Presence of _DCK indicates a docking station.
		 */
		if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, 
			"_DCK", &acpi_handle))) {
			device->flags |= BM_FLAGS_DOCKING_STATION;
		}

		/*
		 * Presence of _EJD and/or _EJx indicates 'ejectable'.
		 * TBD: _EJx...
		 */
		if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, 
			"_EJD", &acpi_handle))) {
			device->flags |= BM_FLAGS_EJECTABLE;
		}

		/*
		 * Presence of _PR0 or _PS0 indicates 'power manageable'.
		 */
		if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, 
			"_PR0", &acpi_handle)) ||
			ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, 
			"_PS0", &acpi_handle))) {
			device->flags |= BM_FLAGS_POWER_CONTROL;
		}

		/*
		 * Presence of _CRS indicates 'configurable'.
		 */
		if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, 
			"_CRS", &acpi_handle))) {
			device->flags |= BM_FLAGS_CONFIGURABLE;
		}

		/* Fall through to next case statement. */

	case BM_TYPE_PROCESSOR:
	case BM_TYPE_THERMAL_ZONE:
	case BM_TYPE_POWER_RESOURCE:
		/*
		 * Presence of _HID or _ADR indicates 'identifiable'.
		 */
		if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, 
			"_HID", &acpi_handle)) ||
		   ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, 
		   "_ADR", &acpi_handle))) {
			device->flags |= BM_FLAGS_IDENTIFIABLE;
		}

		/*
		 * Presence of _STA indicates 'dynamic status'.
		 */
		if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, 
			"_STA", &acpi_handle))) {
			device->flags |= BM_FLAGS_DYNAMIC_STATUS;
		}

		break;
	}

	return AE_OK;
}


/****************************************************************************
 *
 * FUNCTION:    bm_add_namespace_device
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_add_namespace_device (
	ACPI_HANDLE             acpi_handle,
	ACPI_OBJECT_TYPE        acpi_type,
	BM_NODE			*parent,
	BM_NODE			**child)
{
	ACPI_STATUS             status = AE_OK;
	BM_NODE			*node = NULL;
	BM_DEVICE		*device = NULL;

	FUNCTION_TRACE("bm_add_namespace_device");

	if (!parent || !child) {
		return_ACPI_STATUS(AE_BAD_PARAMETER);
	}

	if (node_list.count > BM_HANDLES_MAX) {
		return_ACPI_STATUS(AE_NO_MEMORY);
	}

	(*child) = NULL;

	/*
	 * Create Node:
	 * ------------
	 */
	node = acpi_os_callocate(sizeof(BM_NODE));
	if (!node) {
		return_ACPI_STATUS(AE_NO_MEMORY);
	}

	node->parent = parent;
	node->next = NULL;

	device = &(node->device);

	device->handle = node_list.count;
	device->acpi_handle = acpi_handle;

	/*
	 * Device Type:
	 * ------------
	 */ 
	switch (acpi_type) {
	case INTERNAL_TYPE_SCOPE:
		device->id.type = BM_TYPE_SCOPE;
		break;
	case ACPI_TYPE_PROCESSOR:
		device->id.type = BM_TYPE_PROCESSOR;
		break;
	case ACPI_TYPE_THERMAL:
		device->id.type = BM_TYPE_THERMAL_ZONE;
		break;
	case ACPI_TYPE_POWER:
		device->id.type = BM_TYPE_POWER_RESOURCE;
		break;
	case ACPI_TYPE_DEVICE:
		device->id.type = BM_TYPE_DEVICE;
		break;
	}

	/*
	 * Get Other Device Info:
	 * ----------------------
	 * But only if this device's parent is present (which implies
	 * this device MAY be present).
	 */
	if (BM_NODE_PRESENT(node->parent)) {
		/*
		 * Device Flags
		 */
		status = bm_get_flags(device);
		if (ACPI_FAILURE(status)) {
			goto end;
		}

		/*
		 * Device Identification
		 */
		status = bm_get_identification(device);
		if (ACPI_FAILURE(status)) {
			goto end;
		}

		/*
		 * Device Status
		 */
		status = bm_get_status(device);
		if (ACPI_FAILURE(status)) {
			goto end;
		}

		/*
		 * Power Management:
		 * -----------------
		 * If this node doesn't provide direct power control  
		 * then we inherit PM capabilities from its parent.
		 *
		 * TBD: Inherit!
		 */
		if (BM_IS_POWER_CONTROL(device)) {
			status = bm_get_pm_capabilities(node);
			if (ACPI_FAILURE(status)) {
				goto end;
			}
		}
	}

end:
	if (ACPI_FAILURE(status)) {
		acpi_os_free(node);
	}
	else {
		/*
		 * Add to the node_list.
		 */
		node_list.nodes[node_list.count++] = node;

		/*
		 * Formulate Hierarchy:
		 * --------------------
		 * Arrange within the namespace by assigning the parent and
		 * adding to the parent device's list of children (scope).
		 */
		if (!parent->scope.head) {
			parent->scope.head = node;
		}
		else {
			if (!parent->scope.tail) {
				(parent->scope.head)->next = node;
			}
			else {
				(parent->scope.tail)->next = node;
			}
		}
		parent->scope.tail = node;

		(*child) = node;
	}

	return_ACPI_STATUS(status);
}


/****************************************************************************
 *
 * FUNCTION:    bm_enumerate_namespace
 *
 * PARAMETERS:  <none>
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_enumerate_namespace (void)
{
	ACPI_STATUS		status = AE_OK;
	ACPI_HANDLE             parent_handle = ACPI_ROOT_OBJECT;
	ACPI_HANDLE             child_handle = NULL;
	BM_NODE			*parent = NULL;
	BM_NODE			*child = NULL;
	ACPI_OBJECT_TYPE        acpi_type = 0;
	u32                     level = 1;

	FUNCTION_TRACE("bm_enumerate_namespace");

	parent = node_list.nodes[0];

	/*
	 * Enumerate ACPI Namespace:
	 * -------------------------
	 * Parse through the ACPI namespace, identify all 'devices',
	 * and create a new entry for each in our collection.
	 */
	while (level > 0) {

		/*
		 * Get the next object at this level.
		 */
		status = acpi_get_next_object(ACPI_TYPE_ANY, parent_handle, child_handle, &child_handle);
		if (ACPI_SUCCESS(status)) {
			/*
			 * TBD: This is a hack to get around the problem 
			 *       identifying scope objects.  Scopes 
			 *       somehow need to be uniquely identified.
			 */
			status = acpi_get_type(child_handle, &acpi_type);
			if (ACPI_SUCCESS(status) && (acpi_type == ACPI_TYPE_ANY)) {
				status = acpi_get_next_object(ACPI_TYPE_ANY, child_handle, 0, NULL);
				if (ACPI_SUCCESS(status)) {
					acpi_type = INTERNAL_TYPE_SCOPE;
				}
			}

			/*
			 * Device?
			 * -------
			 * If this object is a 'device', insert into the
			 * ACPI Bus Manager's local hierarchy and search
			 * the object's scope for any child devices (a
			 * depth-first search).
			 */
			switch (acpi_type) {
			case INTERNAL_TYPE_SCOPE:
			case ACPI_TYPE_DEVICE:
			case ACPI_TYPE_PROCESSOR:
			case ACPI_TYPE_THERMAL:
			case ACPI_TYPE_POWER:
				status = bm_add_namespace_device(child_handle, acpi_type, parent, &child);
				if (ACPI_SUCCESS(status)) {
					status = acpi_get_next_object(ACPI_TYPE_ANY, child_handle, 0, NULL);
					if (ACPI_SUCCESS(status)) {
						level++;
						parent_handle = child_handle;
						child_handle = 0;
						parent = child;
					}
				}
				break;
			}
		}

		/*
		 * Scope Exhausted:
		 * ----------------
		 * No more children in this object's scope, Go back up
		 * in the namespace tree to the object's parent.
		 */
		else {
			level--;
			child_handle = parent_handle;
			acpi_get_parent(parent_handle, 
				&parent_handle);

			if (parent) {
				parent = parent->parent;
			}
			else {
				return_ACPI_STATUS(AE_NULL_ENTRY);
			}
		}
	}

	return_ACPI_STATUS(AE_OK);
}


/****************************************************************************
 *
 * FUNCTION:    bm_add_fixed_feature_device
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_add_fixed_feature_device (
	BM_NODE			*parent,
	BM_DEVICE_TYPE		device_type,
	char			*device_hid)
{
	ACPI_STATUS             status = AE_OK;
	BM_NODE			*node = NULL;

	FUNCTION_TRACE("bm_add_fixed_feature_device");

	if (!parent) {
		return_ACPI_STATUS(AE_BAD_PARAMETER);
	}

	if (node_list.count > BM_HANDLES_MAX) {
		return_ACPI_STATUS(AE_NO_MEMORY);
	}

	/*
	 * Allocate the new device and add to the device array.
	 */
	node = acpi_os_callocate(sizeof(BM_NODE));
	if (!node) {
		return_ACPI_STATUS(AE_NO_MEMORY);
	}

	/*
	 * Get device info.
	 */
	node->device.handle = node_list.count;
	node->device.acpi_handle = ACPI_ROOT_OBJECT;
	node->device.id.type = BM_TYPE_FIXED_BUTTON;
	if (device_hid) {
		MEMCPY((void*)node->device.id.hid, device_hid, 
			sizeof(node->device.id.hid));
	}
	node->device.flags = BM_FLAGS_FIXED_FEATURE;
	node->device.status = BM_STATUS_DEFAULT;
	/* TBD: Device PM capabilities */

	/*
	 * Add to the node_list.
	 */
	node_list.nodes[node_list.count++] = node;

	/*
	 * Formulate Hierarchy:
	 * --------------------
	 * Arrange within the namespace by assigning the parent and
	 * adding to the parent device's list of children (scope).
	 */
	node->parent = parent;
	node->next = NULL;

	if (parent) {
		if (!parent->scope.head) {
			parent->scope.head = node;
		}
		else {
			if (!parent->scope.tail) {
				(parent->scope.head)->next = node;
			}
			else {
				(parent->scope.tail)->next = node;
			}
		}
		parent->scope.tail = node;
	}

	return_ACPI_STATUS(status);
}


/****************************************************************************
 *
 * FUNCTION:    bm_enumerate_fixed_features
 *
 * PARAMETERS:  <none>
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_enumerate_fixed_features (void)
{
	FUNCTION_TRACE("bm_enumerate_fixed_features");

	/*
	 * Root Object:
	 * ------------
	 * Fabricate the root object, which happens to always get a
	 * device_handle of zero.
	 */
	node_list.nodes[0] = acpi_os_callocate(sizeof(BM_NODE));
	if (NULL == (node_list.nodes[0])) {
		return_ACPI_STATUS(AE_NO_MEMORY);
	}

	node_list.nodes[0]->device.handle = BM_HANDLE_ROOT;
	node_list.nodes[0]->device.acpi_handle = ACPI_ROOT_OBJECT;
	node_list.nodes[0]->device.flags = BM_FLAGS_UNKNOWN;
	node_list.nodes[0]->device.status = BM_STATUS_DEFAULT;
	node_list.nodes[0]->device.id.type = BM_TYPE_SYSTEM;
	/* TBD: Get system PM capabilities (Sx states?) */

	node_list.count++;

	/*
	 * Fixed Features:
	 * ---------------
	 * Enumerate fixed-feature devices (e.g. power and sleep buttons).
	 */
	if (acpi_fadt.pwr_button == 0) {
		bm_add_fixed_feature_device(node_list.nodes[0],
			BM_TYPE_FIXED_BUTTON, BM_HID_POWER_BUTTON);
	}

	if (acpi_fadt.sleep_button == 0) {
		bm_add_fixed_feature_device(node_list.nodes[0],
			BM_TYPE_FIXED_BUTTON, BM_HID_SLEEP_BUTTON);
	}

	return_ACPI_STATUS(AE_OK);
}


/****************************************************************************
 *
 * FUNCTION:    bm_get_handle
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_get_handle (
	ACPI_HANDLE             acpi_handle,
	BM_HANDLE               *device_handle)
{
	ACPI_STATUS             status = AE_NOT_FOUND;
	u32			i = 0;

	FUNCTION_TRACE("bm_get_handle");

	if (!device_handle) {
		return_ACPI_STATUS(AE_BAD_PARAMETER);
	}

	*device_handle = BM_HANDLE_UNKNOWN;

	/*
	 * Search all devices for a match on the ACPI handle.
	 */
	for (i=0; i<node_list.count; i++) {

		if (!node_list.nodes[i]) {
			DEBUG_PRINT(ACPI_ERROR, ("Invalid (NULL) node entry [%02x] detected.\n", device_handle));
			status = AE_NULL_ENTRY;
			break;
		}

		if (node_list.nodes[i]->device.acpi_handle == acpi_handle) {
			*device_handle = node_list.nodes[i]->device.handle;
			status = AE_OK;
			break;
		}
	}

	return_ACPI_STATUS(status);
}


/****************************************************************************
 *
 * FUNCTION:    bm_get_node
 *
 * PARAMETERS:  
 *
 * RETURN:      
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_get_node (
	BM_HANDLE               device_handle,
	ACPI_HANDLE             acpi_handle,
	BM_NODE			**node)
{
	ACPI_STATUS             status = AE_OK;

	FUNCTION_TRACE("bm_get_node");

	if (!node) {
		return_ACPI_STATUS(AE_BAD_PARAMETER);
	}

	/* busmgr failed to init, but we're being called by subordinate drivers */
	if (node_list.count < 1) {
		return_ACPI_STATUS(AE_NOT_FOUND);
	}

	/*
	 * If no device handle, resolve acpi handle to device handle.
	 */
	if (!device_handle && acpi_handle) {
		status = bm_get_handle(acpi_handle, &device_handle);
		if (ACPI_FAILURE(status))
			return_ACPI_STATUS(status);
	}

	/*
	 * Valid device handle?
	 */
	if (device_handle > BM_HANDLES_MAX) {
		DEBUG_PRINT(ACPI_ERROR, ("Invalid node handle [%02x] detected.\n", device_handle));
		return_ACPI_STATUS(AE_ERROR);
	}

	*node = node_list.nodes[device_handle];

	/*
	 * Valid node?
	 */
	if (!(*node)) {
		DEBUG_PRINT(ACPI_ERROR, ("Invalid (NULL) node entry [%02x] detected.\n", device_handle));
		return_ACPI_STATUS(AE_NULL_ENTRY);
	}

	return_ACPI_STATUS(AE_OK);
}


/****************************************************************************
 *                            External Functions
 ****************************************************************************/

/****************************************************************************
 *
 * FUNCTION:    bm_initialize
 *
 * PARAMETERS:  <none>
 *
 * RETURN:      Exception code.
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_initialize (void)
{
	ACPI_STATUS             status = AE_OK;
	u32                     start = 0;
	u32                     stop = 0;
	u32                     elapsed = 0;

	FUNCTION_TRACE("bm_initialize");

	MEMSET(&node_list, 0, sizeof(BM_NODE_LIST));

	status = acpi_get_timer(&start);
	if (ACPI_FAILURE(status)) {
		return_ACPI_STATUS(status);
	}

	DEBUG_PRINT(ACPI_INFO, ("Building device hierarchy.\n"));

	/*
	 * Enumerate ACPI fixed-feature devices.
	 */
	status = bm_enumerate_fixed_features();
	if (ACPI_FAILURE(status)) {
		return_ACPI_STATUS(status);
	}

	/*
	 * Enumerate the ACPI namespace.
	 */
	status = bm_enumerate_namespace();
	if (ACPI_FAILURE(status)) {
		return_ACPI_STATUS(status);
	}

	acpi_get_timer(&stop);
	acpi_get_timer_duration(start, stop, &elapsed);

	DEBUG_PRINT(ACPI_INFO, ("Building device hierarchy took [%d] microseconds.\n", elapsed));

	/*
	 * Display hierarchy.
	 */
	bm_print_hierarchy();

	/*
	 * Register for all standard and device-specific notifications.
	 */
	DEBUG_PRINT(ACPI_INFO, ("Registering for all device notifications.\n"));

	status = acpi_install_notify_handler(ACPI_ROOT_OBJECT, 
		ACPI_SYSTEM_NOTIFY, &bm_notify, NULL);
	if (ACPI_FAILURE(status)) {
		DEBUG_PRINT(ACPI_ERROR, ("Unable to register for standard notifications.\n"));
		return_ACPI_STATUS(status);
	}

	status = acpi_install_notify_handler(ACPI_ROOT_OBJECT, 
		ACPI_DEVICE_NOTIFY, &bm_notify, NULL);
	if (ACPI_FAILURE(status)) {
		DEBUG_PRINT(ACPI_ERROR, ("Unable to register for device-specific notifications.\n"));
		return_ACPI_STATUS(status);
	}

	DEBUG_PRINT(ACPI_INFO, ("ACPI Bus Manager enabled.\n"));

	/*
	 * Initialize built-in power resource driver.
	 */
	bm_pr_initialize();

	return_ACPI_STATUS(status);
}


/****************************************************************************
 *
 * FUNCTION:    bm_terminate
 *
 * PARAMETERS:  <none>
 *
 * RETURN:      Exception code.
 *
 * DESCRIPTION: 
 *
 ****************************************************************************/

ACPI_STATUS
bm_terminate (void)
{
	ACPI_STATUS             status = AE_OK;
	u32                     i = 0;

	FUNCTION_TRACE("bm_terminate");

	/*
	 * Terminate built-in power resource driver.
	 */
	bm_pr_terminate();

	/*
	 * Unregister for all notifications.
	 */
	DEBUG_PRINT(ACPI_INFO, ("Unregistering for device notifications.\n"));

	status = acpi_remove_notify_handler(ACPI_ROOT_OBJECT, 
		ACPI_SYSTEM_NOTIFY, &bm_notify);
	if (ACPI_FAILURE(status)) {
		DEBUG_PRINT(ACPI_ERROR, ("Unable to un-register for standard notifications.\n"));
	}

	status = acpi_remove_notify_handler(ACPI_ROOT_OBJECT, 
		ACPI_DEVICE_NOTIFY, &bm_notify);
	if (ACPI_FAILURE(status)) {
		DEBUG_PRINT(ACPI_ERROR, ("Unable to un-register for device-specific notifications.\n"));
	}

	/*
	 * Parse through the device array, freeing all entries.
	 */
	DEBUG_PRINT(ACPI_INFO, ("Removing device hierarchy.\n"));
	for (i = 0; i < node_list.count; i++) {
		if (node_list.nodes[i]) {
			acpi_os_free(node_list.nodes[i]);
		}
	}

	DEBUG_PRINT(ACPI_INFO, ("ACPI Bus Manager disabled.\n"));

	return_ACPI_STATUS(AE_OK);
}
