blob: f0e68158b1bb4f270c799bfeb9ecc087aa157244 [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 <memory>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/platform-defs.h>
#include <ddktl/protocol/platform/device.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\n", 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 \n", __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\n", __func__, GPIO_BUTTON);
return -1;
}
while (!done_) {
zxlogf(INFO, "Waiting for GPIO Test Input Interrupt\n");
auto status = interrupt_.wait(nullptr);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: interrupt wait failed %d\n", __func__, status);
return -1;
}
zxlogf(INFO, "Received GPIO Test Input Interrupt\n");
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::PDevProtocolClient pdev(parent());
if (!pdev.is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
pdev_device_info_t info;
if (pdev.GetDeviceInfo(&info) != ZX_OK) {
return ZX_ERR_NOT_SUPPORTED;
}
gpio_count_ = info.gpio_count;
fbl::AllocChecker ac;
gpios_ = fbl::Array(new (&ac) ddk::GpioProtocolClient[info.gpio_count], info.gpio_count);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
for (uint32_t i = 0; i < info.gpio_count; i++) {
auto* gpio = &gpios_[i];
size_t actual;
auto status = pdev.GetProtocol(ZX_PROTOCOL_GPIO, i, gpio, sizeof(*gpio), &actual);
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 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_PDEV),
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)