1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <assert.h> 6#include <stdint.h> 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10#include <threads.h> 11#include <unistd.h> 12 13#include <ddk/binding.h> 14#include <ddk/debug.h> 15#include <ddk/device.h> 16#include <ddk/driver.h> 17#include <ddk/protocol/gpio.h> 18#include <ddk/protocol/platform-defs.h> 19#include <ddk/protocol/platform-device.h> 20 21#include <zircon/process.h> 22#include <zircon/syscalls.h> 23#include <zircon/assert.h> 24 25typedef struct { 26 zx_device_t* zxdev; 27 gpio_protocol_t* gpios; 28 uint32_t gpio_count; 29 thrd_t thread; 30 thrd_t wait; 31 bool done; 32 zx_handle_t inth; 33} gpio_test_t; 34 35// GPIO indices (based on gpio_test_gpios) 36enum { 37 GPIO_LED, 38 GPIO_BUTTON, 39}; 40 41static void gpio_test_release(void* ctx) { 42 gpio_test_t* gpio_test = ctx; 43 gpio_protocol_t* gpios = gpio_test->gpios; 44 45 gpio_test->done = true; 46 zx_handle_close(gpio_test->inth); 47 gpio_release_interrupt(&gpios[GPIO_BUTTON]); 48 thrd_join(gpio_test->thread, NULL); 49 thrd_join(gpio_test->wait, NULL); 50 free(gpio_test->gpios); 51 free(gpio_test); 52} 53 54static zx_protocol_device_t gpio_test_device_protocol = { 55 .version = DEVICE_OPS_VERSION, 56 .release = gpio_test_release, 57}; 58 59// test thread that cycles all of the GPIOs provided to us 60static int gpio_test_thread(void *arg) { 61 gpio_test_t* gpio_test = arg; 62 gpio_protocol_t* gpios = gpio_test->gpios; 63 uint32_t gpio_count = gpio_test->gpio_count; 64 65 for (unsigned i = 0; i < gpio_count; i++) { 66 if (gpio_config_out(&gpios[i], 0) != ZX_OK) { 67 zxlogf(ERROR, "gpio-test: gpio_config failed for gpio %u\n", i); 68 return -1; 69 } 70 } 71 72 while (!gpio_test->done) { 73 // Assuming here that the last GPIO is the input button 74 // so we don't toggle that one 75 for (unsigned i = 0; i < gpio_count-1; i++) { 76 gpio_write(&gpios[i], 1); 77 sleep(1); 78 gpio_write(&gpios[i], 0); 79 sleep(1); 80 } 81 } 82 83 return 0; 84} 85 86static int gpio_waiting_thread(void *arg) { 87 gpio_test_t* gpio_test = arg; 88 gpio_protocol_t* gpios = gpio_test->gpios; 89 while(1) { 90 zxlogf(INFO, "Waiting for GPIO Test Input Interrupt\n"); 91 zx_status_t status = zx_interrupt_wait(gpio_test->inth, NULL); 92 if (status != ZX_OK) { 93 zxlogf(ERROR, "gpio_waiting_thread: zx_interrupt_wait failed %d\n", status); 94 return -1; 95 } 96 zxlogf(INFO, "Received GPIO Test Input Interrupt\n"); 97 uint8_t out; 98 gpio_read(&gpios[GPIO_LED], &out); 99 gpio_write(&gpios[GPIO_LED], !out); 100 sleep(1); 101 } 102} 103 104// test thread that cycles runs tests for GPIO interrupts 105static int gpio_interrupt_test(void *arg) { 106 gpio_test_t* gpio_test = arg; 107 gpio_protocol_t* gpios = gpio_test->gpios; 108 109 if (gpio_config_in(&gpios[GPIO_BUTTON], GPIO_PULL_DOWN) != ZX_OK) { 110 zxlogf(ERROR, "gpio_interrupt_test: gpio_config failed for gpio %u \n", GPIO_BUTTON); 111 return -1; 112 } 113 114 if (gpio_get_interrupt(&gpios[GPIO_BUTTON], 115 ZX_INTERRUPT_MODE_EDGE_HIGH, &gpio_test->inth) != ZX_OK) { 116 zxlogf(ERROR, "gpio_interrupt_test: gpio_get_interrupt failed for gpio %u\n", GPIO_BUTTON); 117 return -1; 118 } 119 120 thrd_create_with_name(&gpio_test->wait, gpio_waiting_thread, gpio_test, "gpio_waiting_thread"); 121 return 0; 122} 123 124// test thread that checks for gpio inputs 125static int gpio_test_in(void *arg) { 126 gpio_test_t* gpio_test = arg; 127 gpio_protocol_t* gpios = gpio_test->gpios; 128 129 if (gpio_config_in(&gpios[GPIO_BUTTON], GPIO_NO_PULL) != ZX_OK) { 130 zxlogf(ERROR, "gpio_interrupt_test: gpio_config failed for gpio %u \n", GPIO_BUTTON); 131 return -1; 132 } 133 134 uint8_t out; 135 while (!gpio_test->done) { 136 gpio_read(&gpios[GPIO_BUTTON], &out); 137 if (out) { 138 zxlogf(INFO, "READ GPIO_BUTTON %u\n",out); 139 sleep(2); 140 } 141 } 142 return 0; 143} 144 145static zx_status_t gpio_test_bind(void* ctx, zx_device_t* parent) { 146 gpio_test_t* gpio_test = calloc(1, sizeof(gpio_test_t)); 147 if (!gpio_test) { 148 return ZX_ERR_NO_MEMORY; 149 } 150 151 platform_device_protocol_t pdev; 152 if (device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &pdev) != ZX_OK) { 153 free(gpio_test); 154 return ZX_ERR_NOT_SUPPORTED; 155 } 156 157 pdev_device_info_t info; 158 if (pdev_get_device_info(&pdev, &info) != ZX_OK) { 159 free(gpio_test); 160 return ZX_ERR_NOT_SUPPORTED; 161 } 162 gpio_test->gpio_count = info.gpio_count; 163 gpio_test->gpios = calloc(info.gpio_count, sizeof(*gpio_test->gpios)); 164 if (!gpio_test->gpios) { 165 free(gpio_test); 166 return ZX_ERR_NO_MEMORY; 167 } 168 for (uint32_t i = 0; i < info.gpio_count; i++) { 169 zx_status_t status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, i, &gpio_test->gpios[i]); 170 if (status != ZX_OK) { 171 free(gpio_test->gpios); 172 free(gpio_test); 173 return status; 174 } 175 } 176 177 device_add_args_t args = { 178 .version = DEVICE_ADD_ARGS_VERSION, 179 .name = "gpio-test", 180 .ctx = gpio_test, 181 .ops = &gpio_test_device_protocol, 182 .flags = DEVICE_ADD_NON_BINDABLE, 183 }; 184 185 zx_status_t status = device_add(parent, &args, NULL); 186 if (status != ZX_OK) { 187 free(gpio_test); 188 return status; 189 } 190 191 thrd_create_with_name(&gpio_test->thread, gpio_test_thread, gpio_test, "gpio_test_thread"); 192 thrd_create_with_name(&gpio_test->thread, gpio_interrupt_test, gpio_test, "gpio_interrupt_test"); 193 return ZX_OK; 194} 195 196static zx_driver_ops_t gpio_test_driver_ops = { 197 .version = DRIVER_OPS_VERSION, 198 .bind = gpio_test_bind, 199}; 200 201ZIRCON_DRIVER_BEGIN(gpio_test, gpio_test_driver_ops, "zircon", "0.1", 4) 202 BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV), 203 BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC), 204 BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC), 205 BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_GPIO_TEST), 206ZIRCON_DRIVER_END(gpio_test) 207