#include <minix/driver.h>
#include <acpi.h>
#include <assert.h>
#include <minix/acpi.h>

#include "pci.h"

PUBLIC int acpi_enabled;
PUBLIC struct machine machine;

/* don't know where ACPI tables are, we may need to access any memory */
PRIVATE int init_mem_priv(void)
{
	struct mem_range mr;

	mr.mr_base = 0;
	mr.mr_limit = 0xffffffff;

	return sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr);
}

PRIVATE void set_machine_mode(void)
{
    ACPI_OBJECT arg1;
    ACPI_OBJECT_LIST args;
    ACPI_STATUS as;

    arg1.Type = ACPI_TYPE_INTEGER;
    arg1.Integer.Value = machine.apic_enabled ? 1 : 0;
    args.Count = 1;
    args.Pointer = &arg1;

    as = AcpiEvaluateObject(ACPI_ROOT_OBJECT, "_PIC", &args, NULL);
    /*
     * We can silently ignore failure as it may not be implemented, ACPI should
     * provide us with correct information anyway
     */
    if (ACPI_SUCCESS(as))
	    printf("ACPI: machine set to %s mode\n",
			    machine.apic_enabled ? "APIC" : "PIC");
}

PRIVATE ACPI_STATUS init_acpica(void)
{
	ACPI_STATUS status;

	status = AcpiInitializeSubsystem();
	if (ACPI_FAILURE(status))
		return status;

	status = AcpiInitializeTables(NULL, 16, FALSE);
	if (ACPI_FAILURE(status))
		return status;

	status = AcpiLoadTables();
	if (ACPI_FAILURE(status))
		return status;

	status = AcpiEnableSubsystem(0);
	if (ACPI_FAILURE(status))
		return status;

	status = AcpiInitializeObjects(0);
	if (ACPI_FAILURE(status))
		return status;

	set_machine_mode();
	
	pci_scan_devices();

	return AE_OK;
}

PUBLIC void init_acpi(void)
{
	ACPI_STATUS acpi_err;
	/* test conditions for acpi */
	if (sys_getmachine(&machine)) {
		printf("ACPI: no machine\n");
		return;
	}
	if (machine.acpi_rsdp == 0) {
		printf("ACPI: no RSDP\n");
		return;
	}
	if (init_mem_priv()) {
		printf("ACPI: no mem access\n");
		return;
	}

	if ((acpi_err = init_acpica()) == AE_OK) {
		acpi_enabled = 1;
		printf("ACPI: ACPI enabled\n");
	}
	else {
		acpi_enabled = 0;
		printf("ACPI: ACPI failed with err %d\n", acpi_err);
	}
}

PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info)
{
	init_acpi();

	return OK;
}

PRIVATE void sef_local_startup()
{
  /* Register init callbacks. */
  sef_setcb_init_fresh(sef_cb_init_fresh);
  sef_setcb_init_lu(sef_cb_init_fresh);
  sef_setcb_init_restart(sef_cb_init_fresh);

  /* Register live update callbacks. */
  sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
  sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);

  /* Let SEF perform startup. */
  sef_startup();
}

int main(void)
{
	int err;
	message m;
	int ipc_status;

	sef_local_startup();

	for(;;) {
		err = driver_receive(ANY, &m, &ipc_status);
		if (err != OK) {
			printf("ACPI: driver_receive failed: %d\n", err);
			continue;
		}

		switch (((struct acpi_request_hdr *)&m)->request) {
		case ACPI_REQ_GET_IRQ:
			do_get_irq(&m);
			break;
		case ACPI_REQ_MAP_BRIDGE:
			do_map_bridge(&m);
			break;
		default:
			printf("ACPI: ignoring unsupported request %d "
				"from %d\n",
				((struct acpi_request_hdr *)&m)->request,
				((struct acpi_request_hdr *)&m)->m_source);
		}

		err = send(m.m_source, &m);
		if (err != OK) {
			printf("ACPI: send failed: %d\n", err);
		}
	}
}
