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/platform-defs.h>
18 #include <ddk/protocol/gpio.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 
25 typedef 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)
36 enum {
37     GPIO_LED,
38     GPIO_BUTTON,
39 };
40 
gpio_test_release(void * ctx)41 static 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 
54 static 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
gpio_test_thread(void * arg)60 static 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 
gpio_waiting_thread(void * arg)86 static 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
gpio_interrupt_test(void * arg)105 static 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
gpio_test_in(void * arg)125 static 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 
gpio_test_bind(void * ctx,zx_device_t * parent)145 static 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     pdev_protocol_t pdev;
152     if (device_get_protocol(parent, ZX_PROTOCOL_PDEV, &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         size_t actual;
170         zx_status_t status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, i, &gpio_test->gpios[i],
171                                                sizeof(gpio_test->gpios[i]), &actual);
172         if (status != ZX_OK) {
173             free(gpio_test->gpios);
174             free(gpio_test);
175             return status;
176         }
177     }
178 
179     device_add_args_t args = {
180         .version = DEVICE_ADD_ARGS_VERSION,
181         .name = "gpio-test",
182         .ctx = gpio_test,
183         .ops = &gpio_test_device_protocol,
184         .flags = DEVICE_ADD_NON_BINDABLE,
185     };
186 
187     zx_status_t status = device_add(parent, &args, NULL);
188     if (status != ZX_OK) {
189         free(gpio_test);
190         return status;
191     }
192 
193     thrd_create_with_name(&gpio_test->thread, gpio_test_thread, gpio_test, "gpio_test_thread");
194     thrd_create_with_name(&gpio_test->thread, gpio_interrupt_test, gpio_test, "gpio_interrupt_test");
195     return ZX_OK;
196 }
197 
198 static zx_driver_ops_t gpio_test_driver_ops = {
199     .version = DRIVER_OPS_VERSION,
200     .bind = gpio_test_bind,
201 };
202 
203 ZIRCON_DRIVER_BEGIN(gpio_test, gpio_test_driver_ops, "zircon", "0.1", 4)
204     BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),
205     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC),
206     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC),
207     BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_GPIO_TEST),
208 ZIRCON_DRIVER_END(gpio_test)
209