| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "processor.h" |
| |
| #include <assert.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| #include <acpica/acpi.h> |
| #include <acpisvc/protocol.h> |
| #include <zircon/syscalls.h> |
| #include <fdio/dispatcher.h> |
| |
| #include "pci.h" |
| #include "power.h" |
| |
| // Data associated with each channel handle |
| typedef struct { |
| // The namespace node associated with this handle. The |
| // handle should be allowed to access ACPI resources further up |
| // the namespace tree. |
| ACPI_HANDLE ns_node; |
| bool root_node; |
| zx_handle_t notify; // event port |
| uint32_t event_mask; |
| uint64_t event_key; |
| } acpi_handle_ctx_t; |
| |
| // Command functions. These should return an error only if the connection |
| // should be aborted. Otherwise they should send their own replies. |
| static zx_status_t cmd_list_children(zx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd); |
| static zx_status_t cmd_get_child_handle(zx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd); |
| static zx_status_t cmd_get_pci_init_arg(zx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd); |
| static zx_status_t cmd_s_state_transition(zx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd); |
| static zx_status_t cmd_ps0(zx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd); |
| static zx_status_t cmd_bst(zx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd); |
| static zx_status_t cmd_bif(zx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd); |
| static zx_status_t cmd_enable_event(zx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd); |
| static zx_status_t cmd_new_connection(zx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd); |
| |
| typedef zx_status_t (*cmd_handler_t)(zx_handle_t, acpi_handle_ctx_t*, void*); |
| static const cmd_handler_t cmd_table[] = { |
| [ACPI_CMD_LIST_CHILDREN] = cmd_list_children, |
| [ACPI_CMD_GET_CHILD_HANDLE] = cmd_get_child_handle, |
| [ACPI_CMD_GET_PCI_INIT_ARG] = cmd_get_pci_init_arg, |
| [ACPI_CMD_S_STATE_TRANSITION] = cmd_s_state_transition, |
| [ACPI_CMD_PS0] = cmd_ps0, |
| [ACPI_CMD_BST] = cmd_bst, |
| [ACPI_CMD_BIF] = cmd_bif, |
| [ACPI_CMD_ENABLE_EVENT] = cmd_enable_event, |
| [ACPI_CMD_NEW_CONNECTION] = cmd_new_connection, |
| }; |
| |
| static zx_status_t send_error(zx_handle_t h, uint32_t req_id, zx_status_t status); |
| |
| static inline uint32_t acpi_event_type(uint16_t events) { |
| if ((events & ACPI_EVENT_SYSTEM_NOTIFY) && (events & ACPI_EVENT_DEVICE_NOTIFY)) { |
| return ACPI_ALL_NOTIFY; |
| } else if (events & ACPI_EVENT_SYSTEM_NOTIFY) { |
| return ACPI_SYSTEM_NOTIFY; |
| } else if (events & ACPI_EVENT_DEVICE_NOTIFY) { |
| return ACPI_DEVICE_NOTIFY; |
| } else { |
| return 0; |
| } |
| } |
| |
| static void notify_handler(ACPI_HANDLE node, uint32_t value, void* _ctx) { |
| acpi_handle_ctx_t* ctx = _ctx; |
| if (ctx->ns_node != node) { |
| return; |
| } |
| uint16_t type; |
| if (value <= 0x7f) { |
| type = ACPI_EVENT_SYSTEM_NOTIFY; |
| } else if (value <= 0xff) { |
| type = ACPI_EVENT_DEVICE_NOTIFY; |
| } else { |
| return; |
| } |
| acpi_event_packet_t pkt = { |
| .pkt_key = ctx->event_key, |
| .version = 0, |
| .type = type, |
| .arg = value, |
| }; |
| zx_port_queue(ctx->notify, &pkt, 0); |
| } |
| |
| static fdio_dispatcher_t* dispatcher; |
| static zx_status_t dispatch(zx_handle_t h, void* _ctx, void* cookie) { |
| acpi_handle_ctx_t* ctx = _ctx; |
| |
| // Check if handle is closed |
| if (h == 0) { |
| if (ctx->notify != ZX_HANDLE_INVALID) { |
| AcpiRemoveNotifyHandler(ctx->ns_node, acpi_event_type(ctx->event_mask), notify_handler); |
| zx_handle_close(ctx->notify); |
| } |
| free(ctx); |
| return ZX_OK; |
| } |
| |
| uint32_t num_bytes = 0; |
| uint32_t num_handles = 0; |
| zx_status_t status = zx_channel_read(h, 0, NULL, NULL, 0, 0, &num_bytes, &num_handles); |
| if (status == ZX_ERR_BAD_STATE) { |
| return ERR_DISPATCHER_NO_WORK; |
| } else if (status != ZX_ERR_BUFFER_TOO_SMALL || |
| num_handles > 1 || |
| num_bytes > ACPI_MAX_REQUEST_SIZE) { |
| // Trigger a close on our end |
| return status; |
| } |
| |
| zx_handle_t cmd_handle = 0; |
| uint8_t buf[ACPI_MAX_REQUEST_SIZE]; |
| status = zx_channel_read(h, 0, buf, &cmd_handle, num_bytes, |
| num_handles, &num_bytes, &num_handles); |
| if (status != ZX_OK) { |
| goto cleanup; |
| } |
| |
| // Validate we have at least a command header |
| if (num_bytes < sizeof(acpi_cmd_hdr_t)) { |
| status = ZX_ERR_INVALID_ARGS; |
| goto cleanup; |
| } |
| |
| acpi_cmd_hdr_t* hdr = (acpi_cmd_hdr_t*)buf; |
| if (hdr->version != 0) { |
| status = send_error(h, hdr->request_id, ZX_ERR_NOT_SUPPORTED); |
| goto cleanup; |
| } |
| if (hdr->len != num_bytes) { |
| status = send_error(h, hdr->request_id, ZX_ERR_INVALID_ARGS); |
| goto cleanup; |
| } |
| |
| // Dispatch the actual command |
| if (hdr->cmd >= countof(cmd_table) || cmd_table[hdr->cmd] == NULL) { |
| status = send_error(h, hdr->request_id, ZX_ERR_NOT_SUPPORTED); |
| goto cleanup; |
| } |
| if (hdr->cmd == ACPI_CMD_ENABLE_EVENT && num_handles == 0) { |
| status = send_error(h, hdr->request_id, ZX_ERR_INVALID_ARGS); |
| goto cleanup; |
| } |
| if (num_handles > 0) { |
| if (hdr->cmd == ACPI_CMD_NEW_CONNECTION) { |
| acpi_handle_ctx_t* context = calloc(1, sizeof(acpi_handle_ctx_t)); |
| if (context == NULL) { |
| status = ZX_ERR_NO_MEMORY; |
| goto cleanup; |
| } |
| context->root_node = ctx->root_node; |
| context->ns_node = ctx->ns_node; |
| if ((status = fdio_dispatcher_add(dispatcher, cmd_handle, context, NULL)) < 0) { |
| free(context); |
| goto cleanup; |
| } |
| acpi_rsp_hdr_t rsp; |
| rsp.status = ZX_OK; |
| rsp.len = sizeof(rsp); |
| rsp.request_id = hdr->request_id; |
| return zx_channel_write(h, 0, &rsp, sizeof(rsp), NULL, 0); |
| } else if (hdr->cmd == ACPI_CMD_ENABLE_EVENT) { |
| if (ctx->notify != ZX_HANDLE_INVALID) { |
| status = ZX_ERR_ALREADY_EXISTS; |
| goto cleanup; |
| } |
| // Set the notify handle here because the command table doesn't accept a handle |
| // in the parameter. |
| ctx->notify = cmd_handle; |
| // Fall through to call the command table |
| } else { |
| status = ZX_ERR_INVALID_ARGS; |
| goto cleanup; |
| } |
| } |
| return cmd_table[hdr->cmd](h, ctx, buf); |
| |
| cleanup: |
| if (cmd_handle > 0) { |
| zx_handle_close(cmd_handle); |
| } |
| return status; |
| } |
| |
| // Launch the main event loop |
| zx_status_t begin_processing(zx_handle_t acpi_root) { |
| acpi_handle_ctx_t* root_context = calloc(1, sizeof(acpi_handle_ctx_t)); |
| if (!root_context) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| zx_status_t status = ZX_ERR_BAD_STATE; |
| ACPI_STATUS acpi_status = AcpiGetHandle(NULL, |
| (char*)"\\_SB", |
| &root_context->ns_node); |
| if (acpi_status != AE_OK) { |
| status = ZX_ERR_NOT_FOUND; |
| goto fail; |
| } |
| root_context->root_node = true; |
| |
| status = fdio_dispatcher_create(&dispatcher, dispatch); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| status = fdio_dispatcher_add(dispatcher, acpi_root, root_context, NULL); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| fdio_dispatcher_run(dispatcher); |
| // fdio_dispatcher_run should not return |
| return ZX_ERR_BAD_STATE; |
| |
| fail: |
| free(root_context); |
| return status; |
| } |
| |
| bool is_pnp_acpi_id(char* buf, unsigned int len) { |
| if (len < 7) { |
| return false; |
| } |
| |
| if (!memcmp(buf, "PNP", 3) && len == 7) { |
| // Check if valid PNP ID |
| return isxdigit(buf[3]) && isxdigit(buf[4]) && |
| isxdigit(buf[5]) && isxdigit(buf[6]); |
| } else if (len == 8) { |
| // Check if valid ACPI ID |
| return (isupper(buf[0]) || isdigit(buf[0])) && |
| (isupper(buf[1]) || isdigit(buf[1])) && |
| (isupper(buf[2]) || isdigit(buf[2])) && |
| (isupper(buf[3]) || isdigit(buf[3])) && |
| isxdigit(buf[4]) && isxdigit(buf[5]) && |
| isxdigit(buf[6]) && isxdigit(buf[7]); |
| } |
| |
| return false; |
| } |
| |
| // Check if *name* is a valid ACPI name |
| static bool is_valid_name(char name[4]) { |
| return (isalnum(name[0]) || name[0] == '_') && |
| (isalnum(name[1]) || name[1] == '_') && |
| (isalnum(name[2]) || name[2] == '_') && |
| (isalnum(name[3]) || name[3] == '_'); |
| } |
| |
| // Send an error response. |
| static zx_status_t send_error(zx_handle_t h, uint32_t req_id, zx_status_t status) { |
| acpi_rsp_hdr_t rsp = { |
| .status = status, |
| .len = sizeof(rsp), |
| .request_id = req_id, |
| }; |
| |
| return zx_channel_write(h, 0, &rsp, sizeof(rsp), NULL, 0); |
| } |
| |
| static zx_status_t cmd_list_children(zx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) { |
| zx_status_t status = ZX_OK; |
| |
| acpi_cmd_list_children_t* cmd = _cmd; |
| if (cmd->hdr.len != sizeof(*cmd)) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INVALID_ARGS); |
| } |
| |
| // Begin by finding the number of children |
| uint32_t num_children = 0; |
| ACPI_HANDLE child = NULL; |
| while (1) { |
| ACPI_STATUS acpi_status = AcpiGetNextObject( |
| ACPI_TYPE_DEVICE, ctx->ns_node, child, &child); |
| if (acpi_status == AE_NOT_FOUND) { |
| break; |
| } |
| if (acpi_status != AE_OK) { |
| return ZX_ERR_BAD_STATE; |
| } |
| num_children++; |
| } |
| |
| acpi_rsp_list_children_t* rsp = NULL; |
| |
| const uint32_t rsp_size = sizeof(*rsp) + sizeof(rsp->children[0]) * num_children; |
| rsp = calloc(1, rsp_size); |
| if (!rsp) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_NO_MEMORY); |
| } |
| |
| rsp->hdr.status = ZX_OK; |
| rsp->hdr.len = rsp_size; |
| rsp->hdr.request_id = cmd->hdr.request_id, |
| rsp->num_children = num_children; |
| |
| num_children = 0; |
| child = NULL; |
| while (num_children < rsp->num_children) { |
| ACPI_STATUS acpi_status = AcpiGetNextObject( |
| ACPI_TYPE_DEVICE, ctx->ns_node, child, &child); |
| if (acpi_status == AE_NOT_FOUND) { |
| break; |
| } else if (acpi_status != AE_OK) { |
| status = ZX_ERR_BAD_STATE; |
| goto cleanup; |
| } |
| |
| ACPI_DEVICE_INFO* info = NULL; |
| acpi_status = AcpiGetObjectInfo(child, &info); |
| if (acpi_status == AE_NO_MEMORY) { |
| status = send_error(h, cmd->hdr.request_id, ZX_ERR_NO_MEMORY); |
| goto cleanup; |
| } else if (acpi_status != AE_OK) { |
| status = ZX_ERR_BAD_STATE; |
| goto cleanup; |
| } |
| |
| // Populate name |
| memcpy(rsp->children[num_children].name, &info->Name, 4); |
| |
| // Populate HID |
| if (info->Valid & ACPI_VALID_HID) { |
| // Add 1 since the Length values count null bytes |
| if (is_pnp_acpi_id(info->HardwareId.String, |
| info->HardwareId.Length - 1)) { |
| |
| assert(info->HardwareId.Length <= sizeof(rsp->children[0].cid[0]) + 1); |
| memcpy(rsp->children[num_children].hid, |
| info->HardwareId.String, |
| info->HardwareId.Length - 1); |
| } |
| } |
| |
| // Populate CID list |
| if (info->Valid & ACPI_VALID_CID) { |
| ACPI_PNP_DEVICE_ID_LIST* cid_list = &info->CompatibleIdList; |
| |
| for (uint32_t i = 0, cid_used = 0; |
| i < cid_list->Count && cid_used < countof(rsp->children[0].cid); |
| ++i) { |
| |
| if (!is_pnp_acpi_id(cid_list->Ids[i].String, |
| cid_list->Ids[i].Length - 1)) { |
| continue; |
| } |
| |
| assert(cid_list->Ids[i].Length <= sizeof(rsp->children[0].cid[0]) + 1); |
| memcpy(rsp->children[num_children].cid[cid_used], |
| cid_list->Ids[i].String, |
| cid_list->Ids[i].Length - 1); |
| |
| cid_used++; |
| } |
| } |
| ACPI_FREE(info); |
| |
| num_children++; |
| } |
| |
| // Sanity check that we enumerated the same number as we started with |
| if (num_children != rsp->num_children) { |
| status = ZX_ERR_BAD_STATE; |
| goto cleanup; |
| } |
| |
| status = zx_channel_write(h, 0, rsp, rsp_size, NULL, 0); |
| |
| cleanup: |
| free(rsp); |
| return status; |
| } |
| |
| static zx_status_t cmd_get_child_handle(zx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) { |
| zx_status_t status = ZX_OK; |
| |
| acpi_cmd_get_child_handle_t* cmd = _cmd; |
| if (cmd->hdr.len != sizeof(*cmd) || !is_valid_name(cmd->name)) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INVALID_ARGS); |
| } |
| |
| // Search for child |
| char name[5] = {0}; |
| memcpy(name, cmd->name, sizeof(cmd->name)); |
| ACPI_HANDLE child_ns_node; |
| ACPI_STATUS acpi_status = AcpiGetHandle(ctx->ns_node, name, &child_ns_node); |
| if (acpi_status != AE_OK) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_NOT_FOUND); |
| } |
| |
| // Build a context for the child handle |
| acpi_handle_ctx_t* child_ctx = calloc(1, sizeof(*child_ctx)); |
| if (!child_ctx) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_NO_MEMORY); |
| } |
| child_ctx->ns_node = child_ns_node; |
| child_ctx->root_node = false; |
| |
| zx_handle_t msg_pipe[2]; |
| status = zx_channel_create(0, &msg_pipe[0], &msg_pipe[1]); |
| if (status != ZX_OK) { |
| free(child_ctx); |
| return send_error(h, cmd->hdr.request_id, status); |
| } |
| |
| status = fdio_dispatcher_add(dispatcher, msg_pipe[1], child_ctx, NULL); |
| if (status != ZX_OK) { |
| status = send_error(h, cmd->hdr.request_id, status); |
| goto cleanup; |
| } |
| |
| acpi_rsp_get_child_handle_t rsp = { |
| .hdr = { |
| .status = ZX_OK, |
| .len = sizeof(rsp), |
| .request_id = cmd->hdr.request_id, |
| }, |
| }; |
| |
| status = zx_channel_write(h, 0, &rsp, sizeof(rsp), msg_pipe, 1); |
| if (status != ZX_OK) { |
| goto cleanup; |
| } |
| |
| return ZX_OK; |
| cleanup: |
| zx_handle_close(msg_pipe[0]); |
| zx_handle_close(msg_pipe[1]); |
| free(child_ctx); |
| return status; |
| } |
| |
| static zx_status_t cmd_get_pci_init_arg(zx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) { |
| zx_status_t status = ZX_OK; |
| |
| acpi_cmd_get_pci_init_arg_t* cmd = _cmd; |
| if (cmd->hdr.len != sizeof(*cmd)) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INVALID_ARGS); |
| } |
| |
| acpi_rsp_get_pci_init_arg_t* rsp = NULL; |
| |
| zx_pci_init_arg_t* arg = NULL; |
| uint32_t arg_size; |
| status = get_pci_init_arg(&arg, &arg_size); |
| if (status != ZX_OK) { |
| return send_error(h, cmd->hdr.request_id, status); |
| } |
| |
| uint32_t len = arg_size + offsetof(acpi_rsp_get_pci_init_arg_t, arg); |
| rsp = malloc(len); |
| if (!rsp) { |
| status = send_error(h, cmd->hdr.request_id, ZX_ERR_NO_MEMORY); |
| goto cleanup; |
| } |
| |
| rsp->hdr.status = ZX_OK; |
| rsp->hdr.len = len; |
| rsp->hdr.request_id = cmd->hdr.request_id, |
| memcpy(&rsp->arg, arg, arg_size); |
| |
| status = zx_channel_write(h, 0, rsp, len, NULL, 0); |
| |
| cleanup: |
| free(arg); |
| free(rsp); |
| return status; |
| } |
| |
| static zx_status_t cmd_s_state_transition(zx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) { |
| acpi_cmd_s_state_transition_t* cmd = _cmd; |
| if (cmd->hdr.len != sizeof(*cmd)) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INVALID_ARGS); |
| } |
| |
| if (!ctx->root_node) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_ACCESS_DENIED); |
| } |
| |
| switch (cmd->target_state) { |
| case ACPI_S_STATE_REBOOT: |
| reboot(); |
| break; |
| case ACPI_S_STATE_S5: |
| poweroff(); |
| break; |
| case ACPI_S_STATE_S3: // fall-through since suspend-to-RAM is not yet supported |
| default: |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_NOT_SUPPORTED); |
| } |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INTERNAL); |
| } |
| |
| static zx_status_t cmd_ps0(zx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) { |
| acpi_cmd_ps0_t* cmd = _cmd; |
| if (cmd->hdr.len != sizeof(*cmd)) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INVALID_ARGS); |
| } |
| |
| if (!ctx->root_node) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_ACCESS_DENIED); |
| } |
| |
| cmd->name[sizeof(cmd->name) - 1] = '\0'; |
| ACPI_HANDLE dev; |
| ACPI_STATUS status = AcpiGetHandle(NULL, cmd->name, &dev); |
| if (status != AE_OK) { |
| printf("Failed to find path %s\n", cmd->name); |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_NOT_FOUND); |
| } |
| |
| status = AcpiEvaluateObject(dev, (char*)"_PS0", NULL, NULL); |
| if (status != AE_OK) { |
| printf("Failed to find object's PS0 method\n"); |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_NOT_FOUND); |
| } |
| |
| acpi_rsp_ps0_t rsp = { |
| .hdr = { |
| .status = ZX_OK, |
| .len = sizeof(rsp), |
| .request_id = cmd->hdr.request_id, |
| }, |
| }; |
| |
| return zx_channel_write(h, 0, &rsp, sizeof(rsp), NULL, 0); |
| } |
| |
| static zx_status_t cmd_bst(zx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) { |
| acpi_cmd_bst_t* cmd = _cmd; |
| if (cmd->hdr.len != sizeof(*cmd)) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INVALID_ARGS); |
| } |
| |
| ACPI_BUFFER buffer = { |
| .Length = ACPI_ALLOCATE_BUFFER, |
| .Pointer = NULL, |
| }; |
| zx_status_t status = AcpiEvaluateObject(ctx->ns_node, (char*)"_BST", NULL, &buffer); |
| if (status != AE_OK) { |
| printf("Failed to find object's BST method\n"); |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_NOT_FOUND); |
| } |
| |
| ACPI_OBJECT* obj = (ACPI_OBJECT*)buffer.Pointer; |
| if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count != 4) { |
| ACPI_FREE(obj); |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INTERNAL); |
| } |
| ACPI_OBJECT* elem = obj->Package.Elements; |
| for (int i = 0; i < 4; i++) { |
| if (elem[i].Type != ACPI_TYPE_INTEGER) { |
| ACPI_FREE(obj); |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INTERNAL); |
| } |
| } |
| |
| acpi_rsp_bst_t rsp = { |
| .hdr = { |
| .status = ZX_OK, |
| .len = sizeof(rsp), |
| .request_id = cmd->hdr.request_id, |
| }, |
| .state = elem[0].Integer.Value, |
| .rate_present = elem[1].Integer.Value, |
| .capacity_remaining = elem[2].Integer.Value, |
| .voltage_present = elem[3].Integer.Value, |
| }; |
| ACPI_FREE(obj); |
| |
| return zx_channel_write(h, 0, &rsp, sizeof(rsp), NULL, 0); |
| } |
| |
| static zx_status_t cmd_bif(zx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) { |
| acpi_cmd_bif_t* cmd = _cmd; |
| if (cmd->hdr.len != sizeof(*cmd)) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INVALID_ARGS); |
| } |
| |
| ACPI_BUFFER buffer = { |
| .Length = ACPI_ALLOCATE_BUFFER, |
| .Pointer = NULL, |
| }; |
| zx_status_t status = AcpiEvaluateObject(ctx->ns_node, (char*)"_BIF", NULL, &buffer); |
| if (status != AE_OK) { |
| printf("Failed to find object's BIF method\n"); |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_NOT_FOUND); |
| } |
| |
| ACPI_OBJECT* obj = (ACPI_OBJECT*)buffer.Pointer; |
| if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count != 13) { |
| ACPI_FREE(obj); |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INTERNAL); |
| } |
| ACPI_OBJECT* elem = obj->Package.Elements; |
| for (int i = 0; i < 9; i++) { |
| if (elem[i].Type != ACPI_TYPE_INTEGER) { |
| ACPI_FREE(obj); |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INTERNAL); |
| } |
| } |
| for (int i = 9; i < 13; i++) { |
| if (elem[i].Type != ACPI_TYPE_STRING) { |
| ACPI_FREE(obj); |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INTERNAL); |
| } |
| } |
| |
| acpi_rsp_bif_t rsp = { |
| .hdr = { |
| .status = ZX_OK, |
| .len = sizeof(rsp), |
| .request_id = cmd->hdr.request_id, |
| }, |
| .power_unit = elem[0].Integer.Value, |
| .capacity_design = elem[1].Integer.Value, |
| .capacity_full = elem[2].Integer.Value, |
| .technology = elem[3].Integer.Value, |
| .voltage_design = elem[4].Integer.Value, |
| .capacity_warning = elem[5].Integer.Value, |
| .capacity_low = elem[6].Integer.Value, |
| .capacity_granularity = elem[7].Integer.Value, |
| .capacity_granularity2 = elem[8].Integer.Value, |
| }; |
| strncpy(rsp.model, elem[9].String.Pointer, sizeof(rsp.model)); |
| strncpy(rsp.serial, elem[10].String.Pointer, sizeof(rsp.serial)); |
| strncpy(rsp.type, elem[11].String.Pointer, sizeof(rsp.type)); |
| strncpy(rsp.oem, elem[12].String.Pointer, sizeof(rsp.oem)); |
| rsp.model[sizeof(rsp.model)-1] = '\0'; |
| rsp.serial[sizeof(rsp.serial)-1] = '\0'; |
| rsp.type[sizeof(rsp.type)-1] = '\0'; |
| rsp.oem[sizeof(rsp.oem)-1] = '\0'; |
| ACPI_FREE(obj); |
| |
| return zx_channel_write(h, 0, &rsp, sizeof(rsp), NULL, 0); |
| } |
| |
| static zx_status_t cmd_enable_event(zx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) { |
| acpi_cmd_enable_event_t* cmd = _cmd; |
| if (cmd->hdr.len != sizeof(*cmd)) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_INVALID_ARGS); |
| } |
| |
| if (ctx->notify == ZX_HANDLE_INVALID) { |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_BAD_STATE); |
| } |
| |
| uint32_t type; |
| if ((type = acpi_event_type(cmd->type)) == 0) { |
| // FIXME(yky): other ACPI event types |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| uint16_t old_mask = ctx->event_mask; |
| uint64_t old_key = ctx->event_key; |
| ctx->event_mask = cmd->type; |
| ctx->event_key = cmd->key; |
| |
| ACPI_STATUS acpi_status = AcpiInstallNotifyHandler(ctx->ns_node, type, notify_handler, ctx); |
| if (acpi_status != AE_OK) { |
| ctx->event_mask = old_mask; |
| ctx->event_key = old_key; |
| return send_error(h, cmd->hdr.request_id, ZX_ERR_BAD_STATE); |
| } |
| |
| acpi_rsp_enable_event_t rsp = { |
| .hdr = { |
| .status = ZX_OK, |
| .len = sizeof(rsp), |
| .request_id = cmd->hdr.request_id, |
| }, |
| }; |
| return zx_channel_write(h, 0, &rsp, sizeof(rsp), NULL, 0); |
| } |
| |
| static zx_status_t cmd_new_connection(zx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) { |
| // if a handle was passed with this, as it should be |
| // this command would have been handled without calling this function |
| return ZX_ERR_INVALID_ARGS; |
| } |