[udev] acpi lid driver

Change-Id: I6d6f33e50ad2e2b16bdd8dd5663b88a765162673
diff --git a/system/core/acpisvc/processor.c b/system/core/acpisvc/processor.c
index 5033a85..be972ba 100644
--- a/system/core/acpisvc/processor.c
+++ b/system/core/acpisvc/processor.c
@@ -37,6 +37,7 @@
 static mx_status_t cmd_ps0(mx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd);
 static mx_status_t cmd_bst(mx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd);
 static mx_status_t cmd_bif(mx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd);
+static mx_status_t cmd_lid(mx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd);
 static mx_status_t cmd_get_event_handle(mx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd);
 static mx_status_t cmd_enable_event(mx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd);
 static mx_status_t cmd_new_connection(mx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd);
@@ -50,6 +51,7 @@
         [ACPI_CMD_PS0] = cmd_ps0,
         [ACPI_CMD_BST] = cmd_bst,
         [ACPI_CMD_BIF] = cmd_bif,
+        [ACPI_CMD_LID] = cmd_lid,
         [ACPI_CMD_GET_EVENT_HANDLE] = cmd_get_event_handle,
         [ACPI_CMD_ENABLE_EVENT] = cmd_enable_event,
         [ACPI_CMD_NEW_CONNECTION] = cmd_new_connection,
@@ -609,6 +611,37 @@
     return mx_channel_write(h, 0, &rsp, sizeof(rsp), NULL, 0);
 }
 
+static mx_status_t cmd_lid(mx_handle_t h, acpi_handle_ctx_t* ctx, void* _cmd) {
+    acpi_cmd_lid_t* cmd = _cmd;
+    if (cmd->hdr.len != sizeof(*cmd)) {
+        return send_error(h, cmd->hdr.request_id, ERR_INVALID_ARGS);
+    }
+
+    ACPI_OBJECT object = {0};
+    ACPI_BUFFER buffer = {
+        .Length = sizeof(object),
+        .Pointer = &object,
+    };
+    ACPI_STATUS status = AcpiEvaluateObject(ctx->ns_node, (char*)"_LID", NULL, &buffer);
+    if (status != AE_OK) {
+        printf("Failed to find object's LID method\n");
+        return send_error(h, cmd->hdr.request_id, ERR_NOT_FOUND);
+    }
+    if (buffer.Length != sizeof(object) || object.Type != ACPI_TYPE_INTEGER) {
+        return send_error(h, cmd->hdr.request_id, ERR_INTERNAL);
+    }
+
+    acpi_rsp_lid_t rsp = {
+        .hdr = {
+            .status = NO_ERROR,
+            .len = sizeof(rsp),
+            .request_id = cmd->hdr.request_id,
+        },
+        .open = (object.Integer.Value != 0),
+    };
+    return mx_channel_write(h, 0, &rsp, sizeof(rsp), NULL, 0);
+}
+
 static mx_status_t cmd_get_event_handle(mx_handle_t h, acpi_handle_ctx_t* ctx, void* cmd) {
     acpi_cmd_hdr_t* hdr = (acpi_cmd_hdr_t*)cmd;
     mx_status_t status;
diff --git a/system/core/devmgr/acpi-device.c b/system/core/devmgr/acpi-device.c
index 9b55515..0884944 100644
--- a/system/core/devmgr/acpi-device.c
+++ b/system/core/devmgr/acpi-device.c
@@ -65,53 +65,69 @@
     return acpi_get_child_handle(h, name, child);
 }
 
+static mx_status_t acpi_init_child_device(mx_device_t* parent, mx_driver_t* drv, acpi_handle_t* h, const char* hid) {
+    acpi_device_t* dev = calloc(1, sizeof(acpi_device_t));
+
+    char name[4];
+    mx_status_t status = acpi_get_child_handle_by_hid(h, hid, &dev->handle, name);
+    if (status != NO_ERROR) {
+        printf("error getting battery handle %d\n", status);
+        free(dev);
+        return status;
+    }
+
+    memcpy(dev->hid, hid, 7);
+    device_init(&dev->device, drv, name, &acpi_device_proto);
+
+    dev->device.protocol_id = MX_PROTOCOL_ACPI;
+    dev->device.protocol_ops = &acpi_device_acpi_proto;
+
+    dev->device.props = calloc(2, sizeof(mx_device_prop_t));
+    dev->device.props[0].id = BIND_ACPI_HID_0_3;
+    dev->device.props[0].value = htobe32(*((uint32_t *)(hid)));
+    dev->device.props[1].id = BIND_ACPI_HID_4_7;
+    dev->device.props[1].value = htobe32(*((uint32_t *)(hid + 4)));
+    dev->device.prop_count = 2;
+
+    if ((status = device_add(&dev->device, parent)) != NO_ERROR) {
+        free(dev->device.props);
+        free(dev);
+    }
+    return status;
+}
+
+#define ACPI_HID_LID     "PNP0C0D"
 #define ACPI_HID_BATTERY "PNP0C0A"
 
 extern mx_handle_t devhost_get_hacpi(void);
 
 static mx_status_t acpi_bind(mx_driver_t* drv, mx_device_t* dev) {
-    // Find the battery device.
-    // TODO(yky,teisenbe) The battery device is in _SB.PCI0 on the acer. To be replaced by real
-    // acpi device publishing code.
+    // TODO(yky,teisenbe) Find the battery device and the lid device. To be replaced by
+    // acpi discovery.
     mx_handle_t hacpi = devhost_get_hacpi();
     if (hacpi <= 0) {
-        printf("no acpi root handle\n");
+        printf("acpi-bus: no acpi root handle\n");
         return ERR_NOT_SUPPORTED;
     }
 
     acpi_handle_t acpi_root, pcie_handle;
     acpi_handle_init(&acpi_root, hacpi);
 
+    if (acpi_init_child_device(dev, drv, &acpi_root, ACPI_HID_LID) == NO_ERROR) {
+        printf("acpi-bus: added lid device\n");
+    }
+
+    // TODO(yky,teisenbe) The battery device is in _SB.PCI0 on the acer.
     mx_status_t status = acpi_get_child_handle_by_hid(&acpi_root, "PNP0A08", &pcie_handle, NULL);
     if (status != NO_ERROR) {
-        printf("no pcie handle\n");
+        printf("acpi-bus: pcie device not found\n");
         acpi_handle_close(&acpi_root);
         return ERR_NOT_SUPPORTED;
     }
     acpi_handle_close(&acpi_root);
 
-    acpi_device_t* batt_dev = calloc(1, sizeof(acpi_device_t));
-    const char* hid = ACPI_HID_BATTERY;
-    char name[4];
-    status = acpi_get_child_handle_by_hid(&pcie_handle, hid, &batt_dev->handle, name);
-    if (status != NO_ERROR) {
-        printf("error getting battery handle %d\n", status);
-        free(batt_dev);
-    } else {
-        memcpy(batt_dev->hid, hid, 7);
-        device_init(&batt_dev->device, drv, name, &acpi_device_proto);
-
-        batt_dev->device.protocol_id = MX_PROTOCOL_ACPI;
-        batt_dev->device.protocol_ops = &acpi_device_acpi_proto;
-
-        batt_dev->device.props = calloc(2, sizeof(mx_device_prop_t));
-        batt_dev->device.props[0].id = BIND_ACPI_HID_0_3;
-        batt_dev->device.props[0].value = htobe32(*((uint32_t *)(hid)));
-        batt_dev->device.props[1].id = BIND_ACPI_HID_4_7;
-        batt_dev->device.props[1].value = htobe32(*((uint32_t *)(hid + 4)));
-        batt_dev->device.prop_count = 2;
-
-        device_add(&batt_dev->device, dev);
+    if (acpi_init_child_device(dev, drv, &pcie_handle, ACPI_HID_BATTERY) == NO_ERROR) {
+        printf("acpi-bus: added battery device\n");
     }
 
     acpi_handle_close(&pcie_handle);
diff --git a/system/udev/acpi-lid/acpi-lid.c b/system/udev/acpi-lid/acpi-lid.c
new file mode 100644
index 0000000..85007cc
--- /dev/null
+++ b/system/udev/acpi-lid/acpi-lid.c
@@ -0,0 +1,105 @@
+// 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 <ddk/device.h>
+#include <ddk/driver.h>
+#include <ddk/binding.h>
+#include <ddk/protocol/acpi.h>
+#include <ddk/protocol/input.h>
+
+#include <acpisvc/simple.h>
+#include <magenta/types.h>
+#include <magenta/syscalls.h>
+#include <magenta/syscalls/port.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <threads.h>
+
+typedef struct acpi_lid_device {
+    mx_device_t device;
+    acpi_handle_t acpi_handle;
+
+    thrd_t event_thread;
+
+    boot_kbd_report_t report;
+} acpi_lid_device_t;
+
+static mx_protocol_device_t acpi_lid_device_proto = {
+};
+
+static int acpi_lid_event_thread(void* arg) {
+    acpi_lid_device_t* dev = arg;
+    acpi_event_packet_t pkt;
+    printf("acpi_lid: event thread start\n");
+    for (;;) {
+        printf("acpi-lid: waiting\n");
+        mx_status_t status = mx_port_wait(dev->acpi_handle.notify, MX_TIME_INFINITE, &pkt, sizeof(pkt));
+        if (status != NO_ERROR) {
+            continue;
+        }
+        printf("acpi-lid: got event type=0x%x arg=0x%x\n", pkt.type, pkt.arg);
+
+        acpi_rsp_lid_t* rsp;
+        if ((status = acpi_lid(&dev->acpi_handle, &rsp)) != NO_ERROR) {
+            continue;
+        }
+        printf("acpi-lid: open=%d\n", rsp->open);
+        free(rsp);
+    }
+    return 0;
+}
+
+static mx_status_t acpi_lid_bind(mx_driver_t* drv, mx_device_t* dev) {
+    printf("acpi-lid: bind\n");
+
+    mx_acpi_protocol_t* acpi;
+    if (device_get_protocol(dev, MX_PROTOCOL_ACPI, (void**)&acpi)) {
+        return ERR_NOT_SUPPORTED;
+    }
+
+    mx_handle_t handle = acpi->clone_handle(dev);
+    if (handle <= 0) {
+        printf("acpi-lid: error cloning handle (%d)\n", handle);
+        return handle;
+    }
+
+    acpi_lid_device_t* device = calloc(1, sizeof(acpi_lid_device_t));
+    if (!device) {
+        mx_handle_close(handle);
+        return ERR_NO_MEMORY;
+    }
+    acpi_handle_init(&device->acpi_handle, handle);
+
+    mx_status_t status = acpi_enable_event(&device->acpi_handle, ACPI_EVENT_DEVICE_NOTIFY);
+    if (status != NO_ERROR) {
+        printf("acpi-lid: error %d enabling device event\n", status);
+    }
+
+    int rc = thrd_create_with_name(&device->event_thread, acpi_lid_event_thread, device, "acpi-lid-event");
+    if (rc != thrd_success) {
+        printf("acpi-lid: event thread did not start (%d)\n", rc);
+    }
+
+    device_init(&device->device, drv, "acpi-lid", &acpi_lid_device_proto);
+    device_add(&device->device, dev);
+
+    printf("acpi-lid: lid device found\n");
+
+    return NO_ERROR;
+}
+
+mx_driver_t _driver_acpi_lid = {
+    .ops = {
+        .bind = acpi_lid_bind,
+    },
+};
+
+#define ACPI_LID_HID_0_3 0x504e5030 // "PNP0"
+#define ACPI_LID_HID_4_7 0x43304400 // "C0D"
+
+MAGENTA_DRIVER_BEGIN(_driver_acpi_lid, "acpi-lid", "magenta", "0.1", 3)
+    BI_ABORT_IF(NE, BIND_PROTOCOL, MX_PROTOCOL_ACPI),
+    BI_ABORT_IF(NE, BIND_ACPI_HID_0_3, ACPI_LID_HID_0_3),
+    BI_MATCH_IF(EQ, BIND_ACPI_HID_4_7, ACPI_LID_HID_4_7),
+MAGENTA_DRIVER_END(_driver_acpi_lid)
diff --git a/system/udev/acpi-lid/rules.mk b/system/udev/acpi-lid/rules.mk
new file mode 100644
index 0000000..8fd4cf2
--- /dev/null
+++ b/system/udev/acpi-lid/rules.mk
@@ -0,0 +1,17 @@
+# 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.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := driver
+
+MODULE_SRCS := $(LOCAL_DIR)/acpi-lid.c
+
+MODULE_STATIC_LIBS := ulib/acpisvc-client ulib/ddk
+
+MODULE_LIBS := ulib/driver ulib/magenta ulib/musl
+
+include make/module.mk
diff --git a/system/ulib/acpisvc-client/include/acpisvc/protocol.h b/system/ulib/acpisvc-client/include/acpisvc/protocol.h
index 0dcc444..ec99a79 100644
--- a/system/ulib/acpisvc-client/include/acpisvc/protocol.h
+++ b/system/ulib/acpisvc-client/include/acpisvc/protocol.h
@@ -22,6 +22,7 @@
     ACPI_CMD_BIF = 7,
     ACPI_CMD_GET_EVENT_HANDLE = 8,
     ACPI_CMD_ENABLE_EVENT = 9,
+    ACPI_CMD_LID = 10,
 };
 
 typedef struct {
@@ -152,6 +153,14 @@
 
 typedef struct {
     acpi_cmd_hdr_t hdr;
+} __PACKED acpi_cmd_lid_t;
+typedef struct {
+    acpi_rsp_hdr_t hdr;
+    uint32_t open;
+} __PACKED acpi_rsp_lid_t;
+
+typedef struct {
+    acpi_cmd_hdr_t hdr;
     uint16_t type;
 } __PACKED acpi_cmd_enable_event_t;
 typedef struct {
diff --git a/system/ulib/acpisvc-client/include/acpisvc/simple.h b/system/ulib/acpisvc-client/include/acpisvc/simple.h
index 14756d6..650504f 100644
--- a/system/ulib/acpisvc-client/include/acpisvc/simple.h
+++ b/system/ulib/acpisvc-client/include/acpisvc/simple.h
@@ -86,6 +86,11 @@
 // NOTE: this is a temporary interface that will be removed soon.
 mx_status_t acpi_bif(acpi_handle_t* h, acpi_rsp_bif_t** response);
 
+// Execute LID for an ACPI node
+//
+// NOTE: this is a temporary interface that will be removed soon.
+mx_status_t acpi_lid(acpi_handle_t* h, acpi_rsp_lid_t** response);
+
 // Opens a channel to an ACPI node for notifications.
 mx_status_t acpi_enable_event(acpi_handle_t* h, uint16_t events);
 
diff --git a/system/ulib/acpisvc-client/simple.c b/system/ulib/acpisvc-client/simple.c
index a4bd761..9eb8877 100644
--- a/system/ulib/acpisvc-client/simple.c
+++ b/system/ulib/acpisvc-client/simple.c
@@ -335,6 +335,28 @@
     return NO_ERROR;
 }
 
+mx_status_t acpi_lid(acpi_handle_t* h, acpi_rsp_lid_t** response) {
+    acpi_cmd_bst_t cmd = {
+        .hdr = {
+            .version = 0,
+            .cmd = ACPI_CMD_LID,
+            .len = sizeof(cmd),
+        },
+    };
+
+    acpi_rsp_lid_t* rsp;
+    size_t rsp_len;
+
+    mx_status_t status =
+        run_txn(h, &cmd, sizeof(cmd), (void**)&rsp, &rsp_len, 0, NULL, 0);
+    if (status != NO_ERROR) {
+        return status;
+    }
+
+    *response = rsp;
+    return NO_ERROR;
+}
+
 mx_status_t acpi_enable_event(acpi_handle_t* _h, uint16_t events) {
     acpi_cmd_hdr_t cmd = {
         .version = 0,