⛏️ index : haiku.git

/*
 * Copyright 2009, Clemens Zeidler. All rights reserved.
 * Copyright 2006, Jérôme Duval. All rights reserved.
 *
 * Distributed under the terms of the MIT License.
 */


#include <stdlib.h>
#include <string.h>


#include "ACPIPrivate.h"
extern "C" {
#include "acpi.h"
}
#include <dpc.h>
#include <PCI.h>


//#define TRACE_ACPI_MODULE
#ifdef TRACE_ACPI_MODULE
#	define TRACE(x) dprintf x
#else
#	define TRACE(x) ;
#endif


device_manager_info* gDeviceManager = NULL;
pci_module_info* gPCIManager = NULL;
dpc_module_info* gDPC = NULL;

module_dependency module_dependencies[] = {
	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager},
	{B_PCI_MODULE_NAME, (module_info**)&gPCIManager},
	{B_DPC_MODULE_NAME, (module_info**)&gDPC},
	{}
};


static float
acpi_module_supports_device(device_node* parent)
{
	// make sure parent is really device root
	const char* bus;
	if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
		return B_ERROR;

	if (strcmp(bus, "root"))
		return 0.0;

	return 1.0;
}


static status_t
acpi_module_register_device(device_node* parent)
{
	device_attr attrs[] = {
		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI" }},
		{ B_DEVICE_FLAGS, B_UINT32_TYPE, { .ui32 = B_KEEP_DRIVER_LOADED }},
		{}
	};

	return gDeviceManager->register_node(parent, ACPI_ROOT_MODULE_NAME, attrs,
		NULL, NULL);
}


static status_t
acpi_enumerate_child_devices(device_node* node, const char* root)
{
	char result[255];
	void* counter = NULL;

	TRACE(("acpi_enumerate_child_devices: recursing from %s\n", root));

	while (get_next_entry(ACPI_TYPE_ANY, root, result,
			sizeof(result), &counter) == B_OK) {
		uint32 type = get_object_type(result);
		device_node* deviceNode;

		switch (type) {
			case ACPI_TYPE_POWER:
			case ACPI_TYPE_PROCESSOR:
			case ACPI_TYPE_THERMAL:
			case ACPI_TYPE_DEVICE: {
				device_attr attrs[16] = {
					// info about device
					{ B_DEVICE_BUS, B_STRING_TYPE, { .string = "acpi" }},

					// location on ACPI bus
					{ ACPI_DEVICE_PATH_ITEM, B_STRING_TYPE, { .string = result }},

					// info about the device
					{ ACPI_DEVICE_TYPE_ITEM, B_UINT32_TYPE, { .ui32 = type }},

					{ B_DEVICE_FLAGS, B_UINT32_TYPE, { .ui32 = B_FIND_MULTIPLE_CHILDREN }},
					{ NULL }
				};

				uint32 attrCount = 4;
				char* hid = NULL;
				char* cidList[11] = { NULL };
				char* uid = NULL;
				char* cls = NULL;
				if (type == ACPI_TYPE_DEVICE) {
					if (get_device_info(result, &hid, (char**)&cidList, 8,
						&uid, &cls) == B_OK) {
						if (hid != NULL) {
							attrs[attrCount].name = ACPI_DEVICE_HID_ITEM;
							attrs[attrCount].type = B_STRING_TYPE;
							attrs[attrCount].value.string = hid;
							attrCount++;
						}
						for (int i = 0; cidList[i] != NULL; i++) {
							attrs[attrCount].name = ACPI_DEVICE_CID_ITEM;
							attrs[attrCount].type = B_STRING_TYPE;
							attrs[attrCount].value.string = cidList[i];
							attrCount++;
						}
						if (uid != NULL) {
							attrs[attrCount].name = ACPI_DEVICE_UID_ITEM;
							attrs[attrCount].type = B_STRING_TYPE;
							attrs[attrCount].value.string = uid;
							attrCount++;
						}
						if (cls != NULL) {
							uint32 clsClass = strtoul(cls, NULL, 16);
							attrs[attrCount].name = B_DEVICE_TYPE;
							attrs[attrCount].type = B_UINT16_TYPE;
							attrs[attrCount].value.ui16 = (clsClass >> 16) & 0xff ;
							attrCount++;
							attrs[attrCount].name = B_DEVICE_SUB_TYPE;
							attrs[attrCount].type = B_UINT16_TYPE;
							attrs[attrCount].value.ui16 = (clsClass >> 8) & 0xff ;
							attrCount++;
							attrs[attrCount].name = B_DEVICE_INTERFACE;
							attrs[attrCount].type = B_UINT16_TYPE;
							attrs[attrCount].value.ui16 = (clsClass >> 0) & 0xff ;
							attrCount++;
						}
					}
					uint32 addr;
					if (get_device_addr(result, &addr) == B_OK) {
						attrs[attrCount].name = ACPI_DEVICE_ADDR_ITEM;
						attrs[attrCount].type = B_UINT32_TYPE;
						attrs[attrCount].value.ui32 = addr;
						attrCount++;
					}
				}

				status_t status = gDeviceManager->register_node(node,
						ACPI_DEVICE_MODULE_NAME, attrs, NULL, &deviceNode);
				free(hid);
				free(uid);
				free(cls);
				for (int i = 0; cidList[i] != NULL; i++)
					free(cidList[i]);
				if (status != B_OK)
					break;
				acpi_enumerate_child_devices(deviceNode, result);
				break;
			}
			default:
				acpi_enumerate_child_devices(node, result);
				break;
		}

	}

	return B_OK;
}


static status_t
acpi_module_register_child_devices(void* cookie)
{
	device_node* node = (device_node*)cookie;

	status_t status = gDeviceManager->publish_device(node, "acpi/namespace",
		ACPI_NS_DUMP_DEVICE_MODULE_NAME);
	if (status != B_OK)
		return status;
	status = gDeviceManager->publish_device(node, "acpi/call",
		ACPI_CALL_DEVICE_MODULE_NAME);
	if (status != B_OK)
		return status;

	if ((AcpiGbl_FADT.Flags & ACPI_FADT_POWER_BUTTON) == 0) {
		dprintf("registering power button\n");
		device_attr attrs[] = {
			// info about device
			{ B_DEVICE_BUS, B_STRING_TYPE, { .string = "acpi" }},

			// info about the device
			{ ACPI_DEVICE_HID_ITEM, B_STRING_TYPE, { .string = "ACPI_FPB" }},
			{ ACPI_DEVICE_TYPE_ITEM, B_UINT32_TYPE, { .ui32 = ACPI_TYPE_DEVICE }},

			// consumer specification
			{ B_DEVICE_FLAGS, B_UINT32_TYPE, { .ui32 = B_FIND_MULTIPLE_CHILDREN }},
			{ NULL }
		};
		device_node* deviceNode;
		gDeviceManager->register_node(node, ACPI_DEVICE_MODULE_NAME, attrs,
				NULL, &deviceNode);
	}
	if ((AcpiGbl_FADT.Flags & ACPI_FADT_SLEEP_BUTTON) == 0) {
		dprintf("registering sleep button\n");
		device_attr attrs[] = {
			// info about device
			{ B_DEVICE_BUS, B_STRING_TYPE, { .string = "acpi" }},

			// info about the device
			{ ACPI_DEVICE_HID_ITEM, B_STRING_TYPE, { .string = "ACPI_FSB" }},
			{ ACPI_DEVICE_TYPE_ITEM, B_UINT32_TYPE, { .ui32 = ACPI_TYPE_DEVICE }},

			// consumer specification
			{ B_DEVICE_FLAGS, B_UINT32_TYPE, { .ui32 = B_FIND_MULTIPLE_CHILDREN }},
			{ NULL }
		};
		device_node* deviceNode;
		gDeviceManager->register_node(node, ACPI_DEVICE_MODULE_NAME, attrs,
				NULL, &deviceNode);

	}

	return acpi_enumerate_child_devices(node, "\\");
}


static status_t
acpi_module_init(device_node* node, void** _cookie)
{
	*_cookie = node;
	return B_OK;
}


static void
acpi_module_uninit(void* cookie)
{
}


static int32
acpi_module_std_ops(int32 op, ...)
{
	switch (op) {
		case B_MODULE_INIT:
		{
			module_info* module;
			return get_module(B_ACPI_MODULE_NAME, &module);
				// this serializes our module initialization
		}

		case B_MODULE_UNINIT:
			return put_module(B_ACPI_MODULE_NAME);
	}

	return B_BAD_VALUE;
}


static struct acpi_root_info sACPIRootModule = {
	{
		{
			ACPI_ROOT_MODULE_NAME,
			0,
			acpi_module_std_ops
		},

		acpi_module_supports_device,
		acpi_module_register_device,
		acpi_module_init,
		acpi_module_uninit,
		acpi_module_register_child_devices,
		NULL,	// rescan devices
		NULL,	// device removed
	},

	get_handle,
	get_name,
	acquire_global_lock,
	release_global_lock,
	install_notify_handler,
	remove_notify_handler,
	update_all_gpes,
	enable_gpe,
	disable_gpe,
	clear_gpe,
	set_gpe,
	finish_gpe,
	install_gpe_handler,
	remove_gpe_handler,
	install_address_space_handler,
	remove_address_space_handler,
	enable_fixed_event,
	disable_fixed_event,
	fixed_event_status,
	reset_fixed_event,
	install_fixed_event_handler,
	remove_fixed_event_handler,
	get_next_entry,
	get_next_object,
	get_device,
	get_device_info,
	get_object_type,
	get_object,
	get_object_typed,
	ns_handle_to_pathname,
	evaluate_object,
	evaluate_method,
	get_irq_routing_table,
	get_current_resources,
	get_possible_resources,
	set_current_resources,
	walk_resources,
	prepare_sleep_state,
	enter_sleep_state,
	reboot,
	get_table
};


module_info* modules[] = {
	(module_info*)&gACPIModule,
	(module_info*)&sACPIRootModule,
	(module_info*)&acpi_ns_dump_module,
	(module_info*)&gACPIDeviceModule,
	(module_info*)&embedded_controller_driver_module,
	(module_info*)&embedded_controller_device_module,
	(module_info*)&gAcpiCallDeviceModule,
	NULL
};