root/src/add-ons/kernel/drivers/display/display_adapter.cpp
#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[B_DEV_NAME_LENGTH];
                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[B_DEV_NAME_LENGTH];
        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
};