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