blob: 4c39a9d546de685ffa0d39b8d550a5a78fc228e5 [file] [log] [blame]
// Copyright 2017 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 "gpio-test.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <memory>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/platform-defs.h>
#include <ddktl/protocol/composite.h>
#include <fbl/alloc_checker.h>
namespace gpio_test {
void GpioTest::DdkRelease() {
done_ = true;
thrd_join(output_thread_, nullptr);
thrd_join(interrupt_thread_, nullptr);
gpios_[GPIO_BUTTON].ReleaseInterrupt();
delete this;
}
// test thread that cycles all of the GPIOs provided to us
int GpioTest::OutputThread() {
for (uint32_t i = 0; i < gpio_count_ - 1; i++) {
if (gpios_[i].ConfigOut(0) != ZX_OK) {
zxlogf(ERROR, "gpio-test: ConfigOut failed for gpio %u", i);
return -1;
}
}
while (!done_) {
// Assuming here that the last GPIO is the input button
// so we don't toggle that one
for (uint32_t i = 0; i < gpio_count_ - 1; i++) {
gpios_[i].Write(1);
sleep(1);
gpios_[i].Write(0);
sleep(1);
}
}
return 0;
}
// test thread that cycles runs tests for GPIO interrupts
int GpioTest::InterruptThread() {
if (gpios_[GPIO_BUTTON].ConfigIn(GPIO_PULL_DOWN) != ZX_OK) {
zxlogf(ERROR, "%s: gpio_config failed for gpio %u ", __func__, GPIO_BUTTON);
return -1;
}
if (gpios_[GPIO_BUTTON].GetInterrupt(ZX_INTERRUPT_MODE_EDGE_HIGH, &interrupt_) != ZX_OK) {
zxlogf(ERROR, "%s: gpio_get_interrupt failed for gpio %u", __func__, GPIO_BUTTON);
return -1;
}
while (!done_) {
zxlogf(INFO, "Waiting for GPIO Test Input Interrupt");
auto status = interrupt_.wait(nullptr);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: interrupt wait failed %d", __func__, status);
return -1;
}
zxlogf(INFO, "Received GPIO Test Input Interrupt");
uint8_t out;
gpios_[GPIO_LED].Read(&out);
gpios_[GPIO_LED].Write(!out);
sleep(1);
}
return 0;
}
zx_status_t GpioTest::Create(void* ctx, zx_device_t* parent) {
fbl::AllocChecker ac;
auto dev = std::unique_ptr<GpioTest>(new (&ac) GpioTest(parent));
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
auto status = dev->Init();
if (status != ZX_OK) {
return status;
}
// devmgr is now in charge of the device.
__UNUSED auto* dummy = dev.release();
return ZX_OK;
}
zx_status_t GpioTest::Init() {
ddk::CompositeProtocolClient composite(parent());
if (!composite.is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
gpio_count_ = composite.GetFragmentCount();
fbl::AllocChecker ac;
gpios_ = fbl::Array(new (&ac) ddk::GpioProtocolClient[gpio_count_], gpio_count_);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
composite_device_fragment_t fragments[gpio_count_];
size_t actual;
composite.GetFragments(fragments, gpio_count_, &actual);
if (actual != gpio_count_) {
return ZX_ERR_INTERNAL;
}
for (uint32_t i = 0; i < gpio_count_; i++) {
auto status = device_get_protocol(fragments[i].device, ZX_PROTOCOL_GPIO, &gpios_[i]);
if (status != ZX_OK) {
return status;
}
}
auto status = DdkAdd("gpio-test", DEVICE_ADD_NON_BINDABLE);
if (status != ZX_OK) {
return status;
}
thrd_create_with_name(
&output_thread_,
[](void* arg) -> int { return reinterpret_cast<GpioTest*>(arg)->OutputThread(); }, this,
"gpio-test output");
thrd_create_with_name(
&interrupt_thread_,
[](void* arg) -> int { return reinterpret_cast<GpioTest*>(arg)->InterruptThread(); }, this,
"gpio-test interrupt");
return ZX_OK;
}
static constexpr zx_driver_ops_t driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = GpioTest::Create;
return ops;
}();
} // namespace gpio_test
ZIRCON_DRIVER_BEGIN(gpio_test, gpio_test::driver_ops, "zircon", "0.1", 4)
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_COMPOSITE),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_GPIO_TEST), ZIRCON_DRIVER_END(gpio_test)