⛏️ index : haiku.git

#include "display_adapter.h"


typedef struct acpi_ns_device_info {
	device_node *node;
	acpi_handle acpi_device;
} displayadapter_device_info;


device_manager_info *gDeviceManager = NULL;
acpi_module_info *gAcpi = NULL;


/*

TODO: ACPI Spec 5 Appendix B: Implement:
_DOS Method to control display output switching
(  _DOD Method to retrieve information about child output devices
	- You can already do this by listing child devices )
_ROM Method to retrieve the ROM image for this device
_GPD Method for determining which VGA device will post
_SPD Method for controlling which VGA device will post
_VPO Method for determining the post options

Display cycling notifications

*/


//	#pragma mark - device module API

	
static status_t
displayadapter_init_device(void *_cookie, void **cookie)
{
	device_node *node = (device_node *)_cookie;
	displayadapter_device_info *device;
//	device_node *parent;

//	acpi_objects arguments;
//	acpi_object_type argument;

	const char *path;
	dprintf("%s: start.\n", __func__);


	device = (displayadapter_device_info *)calloc(1, sizeof(*device));
	if (device == NULL)
		return B_NO_MEMORY;

	device->node = node;
	if (gDeviceManager->get_attr_string(node, ACPI_DEVICE_PATH_ITEM, &path,
			false) != B_OK
		|| gAcpi->get_handle(NULL, path, &device->acpi_device) != B_OK) {
		dprintf("%s: failed to get acpi node.\n", __func__);
		free(device);
		return B_ERROR;
	}
/*
	argument.object_type = ACPI_TYPE_INTEGER;
	argument.integer.integer = BIOS_DISPLAY_SWITCH | BIOS_BRIGHTNESS_CONTROL;
	arguments.count = 1;
	arguments.pointer = &argument;
	if (gAcpi->evaluate_object(&device->acpi_device, "_DOS", &arguments, NULL,
			0) != B_OK)
		dprintf("%s: failed to set _DOS %s\n", __func__, path);

	dprintf("%s: done.\n", __func__);
*/
	*cookie = device;
	return B_OK;
}


static void
displayadapter_uninit_device(void *_cookie)
{
	displayadapter_device_info *device = (displayadapter_device_info *)_cookie;
	free(device);
}


static status_t
displayadapter_open(void *_cookie, const char *path, int flags, void** cookie)
{
	displayadapter_device_info *device = (displayadapter_device_info *)_cookie;
	*cookie = device;
	return B_OK;
}


static status_t
displayadapter_read(void* _cookie, off_t position, void *buf, size_t* num_bytes)
{
	return B_ERROR;
}


static status_t
displayadapter_write(void* cookie, off_t position, const void* buffer,
	size_t* num_bytes)
{
	return B_ERROR;
}


static status_t
displayadapter_control(void* _cookie, uint32 op, void* arg, size_t len)
{
//	displayadapter_device_info* device = (displayadapter_device_info*)_cookie;

	return B_ERROR;
}


static status_t
displayadapter_close(void* cookie)
{
	return B_OK;
}


static status_t
displayadapter_free(void* cookie)
{
	return B_OK;
}


//	#pragma mark - driver module API


static float
displayadapter_support(device_node *parent)
{
	acpi_handle handle, method;
//	acpi_object_type dosType;

	const char *bus;
	const char *path;
	uint32 device_type;

	// make sure parent is really the ACPI bus manager
	if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
		return -1;

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

	if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_PATH_ITEM, &path,
			false) != B_OK)
		return 0.0;

	// check whether it's really a device
	if (gDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
			&device_type, false) != B_OK
		|| device_type != ACPI_TYPE_DEVICE) {
		return 0.0;
	}


	if (gAcpi->get_handle(NULL, path, &handle) != B_OK)
		return 0.0;

	if (gAcpi->get_handle(handle, "_DOD", &method) != B_OK ||
		gAcpi->get_handle(handle, "_DOS", &method) != B_OK) {// ||
//		sAcpi->get_type(method, &dosType) != B_OK ||
//		dosType != ACPI_TYPE_METHOD) {
		return 0.0;
	}

	dprintf("%s: found at bus: %s path: %s\n", __func__, bus, path);
	return 0.6;
}


static status_t
register_displays(const char *parentName, device_node *node)
{
	acpi_handle acpiHandle;
	const char *path;
	device_node *parent = gDeviceManager->get_parent_node(node);
	if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_PATH_ITEM, &path,
			false) != B_OK
		|| gAcpi->get_handle(NULL, path, &acpiHandle) != B_OK) {
		dprintf("%s: failed to get acpi node.\n", __func__);
		gDeviceManager->put_node(parent);
		return B_ERROR;
	}

	//get list of ids from _DOD
	acpi_object_type *pkgData = (acpi_object_type *)malloc(128);
	if (pkgData == NULL)
		return B_ERROR;

	status_t status = gAcpi->evaluate_object(acpiHandle, "_DOD", NULL, pkgData,
		128);
	if (status != B_OK || pkgData->object_type != ACPI_TYPE_PACKAGE) {
		dprintf("%s: fail. %ld %lu\n", __func__, status, pkgData->object_type);
		free(pkgData);
		return status;	
	}

	acpi_object_type *displayIDs = pkgData->package.objects;
	for (uint32 i = 0; i < pkgData->package.count; i++) {
		dprintf("Display ID = %lld\n", displayIDs[i].integer.integer);
	}
    
	acpi_object_type result;
	acpi_handle child = NULL;
	while (gAcpi->get_next_object(ACPI_TYPE_DEVICE, acpiHandle, &child)
		== B_OK) {
		char name[5] = {0};
		//TODO: HARDCODED type.
		if(gAcpi->get_name(child, 1, name, 5) == B_OK) 
			dprintf("name: %s\n", name);
		if (gAcpi->evaluate_object(child, "_ADR", NULL, &result, sizeof(result))
			!= B_OK)
			continue;
		
		dprintf("Child _adr %llu\n", result.integer.integer);
		uint32 i;
		for (i = 0; i < pkgData->package.count; i++)
			if (displayIDs[i].integer.integer == result.integer.integer) break;
		
		if (i == pkgData->package.count) continue;
		
		device_attr attrs[] = {
			{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = name }},
			{ B_DEVICE_FLAGS, B_UINT32_TYPE, { .ui32 = B_KEEP_DRIVER_LOADED }},
			{ NULL }
		
		};
		
		device_node* deviceNode;
		gDeviceManager->register_node(node, DISPLAY_DEVICE_MODULE_NAME, attrs,
				NULL, &deviceNode);

		char deviceName[128];
		snprintf(deviceName, sizeof(deviceName), "%s/%s", parentName, name);
		gDeviceManager->publish_device(parent, deviceName,
			DISPLAY_DEVICE_MODULE_NAME);
		
	}
	gDeviceManager->put_node(parent);
	free(pkgData);
	return B_OK;
}


static status_t
displayadapter_register_device(device_node *node)
{
	device_attr attrs[] = {
		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "Display Adapter" }},
		{ B_DEVICE_FLAGS, B_UINT32_TYPE, {
			.ui32 = B_KEEP_DRIVER_LOADED | B_FIND_MULTIPLE_CHILDREN }},
		{ NULL }
	};

	return gDeviceManager->register_node(node, DISPLAYADAPTER_MODULE_NAME,
		attrs, NULL, NULL);
}


static status_t
displayadapter_init_driver(device_node *node, void **_driverCookie)
{
	*_driverCookie = node;
	return B_OK;
}


static void
displayadapter_uninit_driver(void *driverCookie)
{
}


static status_t
displayadapter_register_child_devices(void *_cookie)
{
	device_node *node = (device_node*)_cookie;

	int path_id = gDeviceManager->create_id(DISPLAYADAPTER_PATHID_GENERATOR);
	if (path_id < 0) {
		dprintf("displayadapter_register_child_devices: error creating path\n");
		return B_ERROR;
	}

	char name[128];
	snprintf(name, sizeof(name), DISPLAYADAPTER_BASENAME, path_id);
	status_t status = gDeviceManager->publish_device(node, name,
		DISPLAYADAPTER_DEVICE_MODULE_NAME);

	if (status == B_OK)
		register_displays(name, node);

	return status;
}


module_dependency module_dependencies[] = {
	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager },
	{ B_ACPI_MODULE_NAME, (module_info **)&gAcpi},
	{}
};


driver_module_info displayadapter_driver_module = {
	{
		DISPLAYADAPTER_MODULE_NAME,
		0,
		NULL
	},

	displayadapter_support,
	displayadapter_register_device,
	displayadapter_init_driver,
	displayadapter_uninit_driver,
	displayadapter_register_child_devices,
	NULL,	// rescan
	NULL,	// removed
};


device_module_info displayadapter_device_module = {
	{
		DISPLAYADAPTER_DEVICE_MODULE_NAME,
		0,
		NULL
	},

	displayadapter_init_device,
	displayadapter_uninit_device,
	NULL,

	displayadapter_open,
	displayadapter_close,
	displayadapter_free,
	displayadapter_read,
	displayadapter_write,
	NULL,
	displayadapter_control,

	NULL,
	NULL
};


module_info *modules[] = {
	(module_info *)&display_device_module,
	(module_info *)&displayadapter_device_module,
	(module_info *)&displayadapter_driver_module,
	NULL
};