// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { zx_device_t* zxdev; gpio_protocol_t* gpios; uint32_t gpio_count; thrd_t thread; thrd_t wait; bool done; zx_handle_t inth; } gpio_test_t; // GPIO indices (based on gpio_test_gpios) enum { GPIO_LED, GPIO_BUTTON, }; static void gpio_test_release(void* ctx) { gpio_test_t* gpio_test = ctx; gpio_protocol_t* gpios = gpio_test->gpios; gpio_test->done = true; zx_handle_close(gpio_test->inth); gpio_release_interrupt(&gpios[GPIO_BUTTON]); thrd_join(gpio_test->thread, NULL); thrd_join(gpio_test->wait, NULL); free(gpio_test->gpios); free(gpio_test); } static zx_protocol_device_t gpio_test_device_protocol = { .version = DEVICE_OPS_VERSION, .release = gpio_test_release, }; // test thread that cycles all of the GPIOs provided to us static int gpio_test_thread(void *arg) { gpio_test_t* gpio_test = arg; gpio_protocol_t* gpios = gpio_test->gpios; uint32_t gpio_count = gpio_test->gpio_count; for (unsigned i = 0; i < gpio_count; i++) { if (gpio_config_out(&gpios[i], 0) != ZX_OK) { zxlogf(ERROR, "gpio-test: gpio_config failed for gpio %u\n", i); return -1; } } while (!gpio_test->done) { // Assuming here that the last GPIO is the input button // so we don't toggle that one for (unsigned i = 0; i < gpio_count-1; i++) { gpio_write(&gpios[i], 1); sleep(1); gpio_write(&gpios[i], 0); sleep(1); } } return 0; } static int gpio_waiting_thread(void *arg) { gpio_test_t* gpio_test = arg; gpio_protocol_t* gpios = gpio_test->gpios; while(1) { zxlogf(INFO, "Waiting for GPIO Test Input Interrupt\n"); zx_status_t status = zx_interrupt_wait(gpio_test->inth, NULL); if (status != ZX_OK) { zxlogf(ERROR, "gpio_waiting_thread: zx_interrupt_wait failed %d\n", status); return -1; } zxlogf(INFO, "Received GPIO Test Input Interrupt\n"); uint8_t out; gpio_read(&gpios[GPIO_LED], &out); gpio_write(&gpios[GPIO_LED], !out); sleep(1); } } // test thread that cycles runs tests for GPIO interrupts static int gpio_interrupt_test(void *arg) { gpio_test_t* gpio_test = arg; gpio_protocol_t* gpios = gpio_test->gpios; if (gpio_config_in(&gpios[GPIO_BUTTON], GPIO_PULL_DOWN) != ZX_OK) { zxlogf(ERROR, "gpio_interrupt_test: gpio_config failed for gpio %u \n", GPIO_BUTTON); return -1; } if (gpio_get_interrupt(&gpios[GPIO_BUTTON], ZX_INTERRUPT_MODE_EDGE_HIGH, &gpio_test->inth) != ZX_OK) { zxlogf(ERROR, "gpio_interrupt_test: gpio_get_interrupt failed for gpio %u\n", GPIO_BUTTON); return -1; } thrd_create_with_name(&gpio_test->wait, gpio_waiting_thread, gpio_test, "gpio_waiting_thread"); return 0; } // test thread that checks for gpio inputs static int gpio_test_in(void *arg) { gpio_test_t* gpio_test = arg; gpio_protocol_t* gpios = gpio_test->gpios; if (gpio_config_in(&gpios[GPIO_BUTTON], GPIO_NO_PULL) != ZX_OK) { zxlogf(ERROR, "gpio_interrupt_test: gpio_config failed for gpio %u \n", GPIO_BUTTON); return -1; } uint8_t out; while (!gpio_test->done) { gpio_read(&gpios[GPIO_BUTTON], &out); if (out) { zxlogf(INFO, "READ GPIO_BUTTON %u\n",out); sleep(2); } } return 0; } static zx_status_t gpio_test_bind(void* ctx, zx_device_t* parent) { gpio_test_t* gpio_test = calloc(1, sizeof(gpio_test_t)); if (!gpio_test) { return ZX_ERR_NO_MEMORY; } platform_device_protocol_t pdev; if (device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &pdev) != ZX_OK) { free(gpio_test); return ZX_ERR_NOT_SUPPORTED; } pdev_device_info_t info; if (pdev_get_device_info(&pdev, &info) != ZX_OK) { free(gpio_test); return ZX_ERR_NOT_SUPPORTED; } gpio_test->gpio_count = info.gpio_count; gpio_test->gpios = calloc(info.gpio_count, sizeof(*gpio_test->gpios)); if (!gpio_test->gpios) { free(gpio_test); return ZX_ERR_NO_MEMORY; } for (uint32_t i = 0; i < info.gpio_count; i++) { zx_status_t status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, i, &gpio_test->gpios[i]); if (status != ZX_OK) { free(gpio_test->gpios); free(gpio_test); return status; } } device_add_args_t args = { .version = DEVICE_ADD_ARGS_VERSION, .name = "gpio-test", .ctx = gpio_test, .ops = &gpio_test_device_protocol, .flags = DEVICE_ADD_NON_BINDABLE, }; zx_status_t status = device_add(parent, &args, NULL); if (status != ZX_OK) { free(gpio_test); return status; } thrd_create_with_name(&gpio_test->thread, gpio_test_thread, gpio_test, "gpio_test_thread"); thrd_create_with_name(&gpio_test->thread, gpio_interrupt_test, gpio_test, "gpio_interrupt_test"); return ZX_OK; } static zx_driver_ops_t gpio_test_driver_ops = { .version = DRIVER_OPS_VERSION, .bind = gpio_test_bind, }; ZIRCON_DRIVER_BEGIN(gpio_test, gpio_test_driver_ops, "zircon", "0.1", 4) BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV), 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)