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