1 // Copyright 2018 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 <string.h>
6 #include <threads.h>
7 #include <unistd.h>
8 
9 #include <ddk/debug.h>
10 #include <ddk/metadata.h>
11 #include <ddk/platform-defs.h>
12 #include <ddk/protocol/platform/bus.h>
13 #include <ddk/protocol/platform/device.h>
14 
15 #include <hid/descriptor.h>
16 
17 #include <fbl/algorithm.h>
18 #include <fbl/auto_call.h>
19 #include <fbl/auto_lock.h>
20 #include <fbl/unique_ptr.h>
21 
22 #include <zircon/syscalls.h>
23 #include <zircon/syscalls/port.h>
24 
25 #include "hid-buttons.h"
26 
27 // clang-format off
28 // zx_port_packet::key.
29 constexpr uint64_t PORT_KEY_SHUTDOWN = 0x01;
30 // Start of up to kNumberOfRequiredGpios port types used for interrupts.
31 constexpr uint64_t PORT_KEY_INTERRUPT_START = 0x10;
32 // clang-format on
33 
34 namespace buttons {
35 
Thread()36 int HidButtonsDevice::Thread() {
37     while (1) {
38         zx_port_packet_t packet;
39         zx_status_t status = port_.wait(zx::time::infinite(), &packet);
40         zxlogf(TRACE, "%s msg received on port key %lu\n", __FUNCTION__, packet.key);
41         if (status != ZX_OK) {
42             zxlogf(ERROR, "%s port wait failed %d\n", __FUNCTION__, status);
43             return thrd_error;
44         }
45 
46         if (packet.key == PORT_KEY_SHUTDOWN) {
47             zxlogf(INFO, "%s shutting down\n", __FUNCTION__);
48             return thrd_success;
49         } else if (packet.key >= PORT_KEY_INTERRUPT_START &&
50                    packet.key < (PORT_KEY_INTERRUPT_START + buttons_.size())) {
51             uint32_t type = static_cast<uint32_t>(packet.key - PORT_KEY_INTERRUPT_START);
52             if (gpios_[type].config.type == BUTTONS_GPIO_TYPE_INTERRUPT) {
53                 // We need to reconfigure the GPIO to catch the opposite polarity.
54                 ReconfigurePolarity(type, packet.key);
55             }
56 
57             buttons_input_rpt_t input_rpt;
58             size_t out_len;
59             status = HidbusGetReport(0, BUTTONS_RPT_ID_INPUT, &input_rpt, sizeof(input_rpt),
60                                      &out_len);
61             if (status != ZX_OK) {
62                 zxlogf(ERROR, "%s HidbusGetReport failed %d\n", __FUNCTION__, status);
63             } else {
64                 fbl::AutoLock lock(&client_lock_);
65                 if (client_.is_valid()) {
66                     client_.IoQueue(&input_rpt, sizeof(buttons_input_rpt_t));
67                     // If report could not be filled, we do not ioqueue.
68                 }
69             }
70             if (fdr_gpio_.has_value() && fdr_gpio_.value() == type) {
71                 zxlogf(INFO, "FDR (up and down buttons) pressed\n");
72             }
73 
74             gpios_[type].irq.ack();
75         }
76     }
77     return thrd_success;
78 }
79 
HidbusStart(const hidbus_ifc_t * ifc)80 zx_status_t HidButtonsDevice::HidbusStart(const hidbus_ifc_t* ifc) {
81     fbl::AutoLock lock(&client_lock_);
82     if (client_.is_valid()) {
83         return ZX_ERR_ALREADY_BOUND;
84     } else {
85         client_ = ddk::HidbusIfcClient(ifc);
86     }
87     return ZX_OK;
88 }
89 
HidbusQuery(uint32_t options,hid_info_t * info)90 zx_status_t HidButtonsDevice::HidbusQuery(uint32_t options, hid_info_t* info) {
91     if (!info) {
92         return ZX_ERR_INVALID_ARGS;
93     }
94     info->dev_num = 0;
95     info->device_class = HID_DEVICE_CLASS_OTHER;
96     info->boot_device = false;
97 
98     return ZX_OK;
99 }
100 
HidbusStop()101 void HidButtonsDevice::HidbusStop() {
102     fbl::AutoLock lock(&client_lock_);
103     client_.clear();
104 }
105 
HidbusGetDescriptor(uint8_t desc_type,void ** data,size_t * len)106 zx_status_t HidButtonsDevice::HidbusGetDescriptor(uint8_t desc_type, void** data, size_t* len) {
107     const uint8_t* desc_ptr;
108     uint8_t* buf;
109     if (!len || !data) {
110         return ZX_ERR_INVALID_ARGS;
111     }
112     *len = get_buttons_report_desc(&desc_ptr);
113     fbl::AllocChecker ac;
114     buf = new (&ac) uint8_t[*len];
115     if (!ac.check()) {
116         return ZX_ERR_NO_MEMORY;
117     }
118     memcpy(buf, desc_ptr, *len);
119     *data = buf;
120     return ZX_OK;
121 }
122 
123 // Requires interrupts to be disabled for all rows/cols.
MatrixScan(uint32_t row,uint32_t col,zx_duration_t delay)124 bool HidButtonsDevice::MatrixScan(uint32_t row, uint32_t col, zx_duration_t delay) {
125 
126     gpio_config_in(&gpios_[col].gpio, GPIO_NO_PULL); // Float column to find row in use.
127     zx::nanosleep(zx::deadline_after(zx::duration(delay)));
128 
129     uint8_t val;
130     gpio_read(&gpios_[row].gpio, &val);
131 
132     gpio_config_out(&gpios_[col].gpio, gpios_[col].config.output_value);
133     zxlogf(TRACE, "%s row %u col %u val %u\n", __FUNCTION__, row, col, val);
134     return static_cast<bool>(val);
135 }
136 
HidbusGetReport(uint8_t rpt_type,uint8_t rpt_id,void * data,size_t len,size_t * out_len)137 zx_status_t HidButtonsDevice::HidbusGetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
138                                               size_t len, size_t* out_len) {
139     if (!data || !out_len) {
140         return ZX_ERR_INVALID_ARGS;
141     }
142     if (rpt_id != BUTTONS_RPT_ID_INPUT) {
143         return ZX_ERR_NOT_SUPPORTED;
144     }
145     *out_len = sizeof(buttons_input_rpt_t);
146     if (*out_len > len) {
147         return ZX_ERR_BUFFER_TOO_SMALL;
148     }
149 
150     buttons_input_rpt_t input_rpt = {};
151     input_rpt.rpt_id = BUTTONS_RPT_ID_INPUT;
152 
153     // Disable interrupts.
154     for (uint32_t i = 0; i < gpios_.size(); ++i) {
155         if (gpios_[i].config.type == BUTTONS_GPIO_TYPE_INTERRUPT) {
156             zx_status_t status = gpio_release_interrupt(&gpios_[i].gpio);
157             if (status != ZX_OK) {
158                 zxlogf(ERROR, "%s gpio_release_interrupt failed %d\n", __FUNCTION__, status);
159                 return status;
160             }
161         }
162     }
163 
164     for (size_t i = 0; i < buttons_.size(); ++i) {
165         bool new_value = false; // A value true means a button is pressed.
166         if (buttons_[i].type == BUTTONS_TYPE_MATRIX) {
167             new_value = MatrixScan(buttons_[i].gpioA_idx, buttons_[i].gpioB_idx,
168                                    buttons_[i].gpio_delay);
169         } else if (buttons_[i].type == BUTTONS_TYPE_DIRECT) {
170             uint8_t val;
171             gpio_read(&gpios_[buttons_[i].gpioA_idx].gpio, &val);
172             zxlogf(TRACE, "%s GPIO direct read %u for button %lu\n", __FUNCTION__, val, i);
173             new_value = val;
174         } else {
175             zxlogf(ERROR, "%s unknown button type %u\n", __FUNCTION__, buttons_[i].type);
176             return ZX_ERR_INTERNAL;
177         }
178 
179         if (gpios_[i].config.flags & BUTTONS_GPIO_FLAG_INVERTED) {
180             new_value = !new_value;
181         }
182 
183         zxlogf(TRACE, "%s GPIO new value %u for button %lu\n", __FUNCTION__, new_value, i);
184         fill_button_in_report(buttons_[i].id, new_value, &input_rpt);
185     }
186     auto out = static_cast<buttons_input_rpt_t*>(data);
187     *out = input_rpt;
188 
189     // Reenable interrupts.
190     for (uint32_t i = 0; i < gpios_.size(); ++i) {
191         if (gpios_[i].config.type == BUTTONS_GPIO_TYPE_INTERRUPT) {
192             zx_status_t status = ConfigureInterrupt(i, PORT_KEY_INTERRUPT_START + i);
193             if (status != ZX_OK) {
194                 return status;
195             }
196         }
197     }
198     return ZX_OK;
199 }
200 
HidbusSetReport(uint8_t rpt_type,uint8_t rpt_id,const void * data,size_t len)201 zx_status_t HidButtonsDevice::HidbusSetReport(uint8_t rpt_type, uint8_t rpt_id, const void* data,
202                                               size_t len) {
203     return ZX_ERR_NOT_SUPPORTED;
204 }
205 
HidbusGetIdle(uint8_t rpt_id,uint8_t * duration)206 zx_status_t HidButtonsDevice::HidbusGetIdle(uint8_t rpt_id, uint8_t* duration) {
207     return ZX_ERR_NOT_SUPPORTED;
208 }
209 
HidbusSetIdle(uint8_t rpt_id,uint8_t duration)210 zx_status_t HidButtonsDevice::HidbusSetIdle(uint8_t rpt_id, uint8_t duration) {
211     return ZX_ERR_NOT_SUPPORTED;
212 }
213 
HidbusGetProtocol(uint8_t * protocol)214 zx_status_t HidButtonsDevice::HidbusGetProtocol(uint8_t* protocol) {
215     return ZX_ERR_NOT_SUPPORTED;
216 }
217 
HidbusSetProtocol(uint8_t protocol)218 zx_status_t HidButtonsDevice::HidbusSetProtocol(uint8_t protocol) {
219     return ZX_OK;
220 }
221 
ReconfigurePolarity(uint32_t idx,uint64_t int_port)222 void HidButtonsDevice::ReconfigurePolarity(uint32_t idx, uint64_t int_port) {
223     zxlogf(TRACE, "%s gpio %u port %lu\n", __FUNCTION__, idx, int_port);
224     uint8_t current = 0, old;
225     gpio_read(&gpios_[idx].gpio, &current);
226     do {
227         gpio_set_polarity(&gpios_[idx].gpio, current ? GPIO_POLARITY_LOW : GPIO_POLARITY_HIGH);
228         old = current;
229         gpio_read(&gpios_[idx].gpio, &current);
230         zxlogf(SPEW, "%s old gpio %u new gpio %u\n", __FUNCTION__, old, current);
231         // If current switches after setup, we setup a new trigger for it (opposite edge).
232     } while (current != old);
233 }
234 
ConfigureInterrupt(uint32_t idx,uint64_t int_port)235 zx_status_t HidButtonsDevice::ConfigureInterrupt(uint32_t idx, uint64_t int_port) {
236     zxlogf(TRACE, "%s gpio %u port %lu\n", __FUNCTION__, idx, int_port);
237     zx_status_t status;
238     uint8_t current = 0;
239     gpio_read(&gpios_[idx].gpio, &current);
240     gpio_release_interrupt(&gpios_[idx].gpio);
241     // We setup a trigger for the opposite of the current GPIO value.
242     status = gpio_get_interrupt(
243         &gpios_[idx].gpio,
244         current ? ZX_INTERRUPT_MODE_EDGE_LOW : ZX_INTERRUPT_MODE_EDGE_HIGH,
245         gpios_[idx].irq.reset_and_get_address());
246     if (status != ZX_OK) {
247         zxlogf(ERROR, "%s gpio_get_interrupt failed %d\n", __FUNCTION__, status);
248         return status;
249     }
250     status = gpios_[idx].irq.bind(port_, int_port, 0);
251     if (status != ZX_OK) {
252         zxlogf(ERROR, "%s zx_interrupt_bind failed %d\n", __FUNCTION__, status);
253         return status;
254     }
255     // To make sure polarity is correct in case it changed during configuration.
256     ReconfigurePolarity(idx, int_port);
257     return ZX_OK;
258 }
259 
Bind()260 zx_status_t HidButtonsDevice::Bind() {
261     zx_status_t status;
262 
263     status = zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port_);
264     if (status != ZX_OK) {
265         zxlogf(ERROR, "%s port_create failed %d\n", __FUNCTION__, status);
266         return status;
267     }
268 
269     pdev_protocol_t pdev;
270     status = device_get_protocol(parent_, ZX_PROTOCOL_PDEV, &pdev);
271     if (status != ZX_OK) {
272         zxlogf(ERROR, "%s device_get_protocol failed %d\n", __FUNCTION__, status);
273         return status;
274     }
275 
276     pdev_device_info_t pdev_info;
277     status = pdev_get_device_info(&pdev, &pdev_info);
278     if (status != ZX_OK) {
279         zxlogf(ERROR, "%s pdev_get_device_info failed %d\n", __FUNCTION__, status);
280         return status;
281     }
282 
283     // TODO(andresoportus): Remove BUTTONS_ID_MAX usage below once we add metadata size probe
284     // capability to devmgr.
285 
286     fbl::AllocChecker ac;
287     // We have up to BUTTONS_ID_MAX available buttons.
288     auto buttons = fbl::Array(new (&ac) buttons_button_config_t[BUTTONS_ID_MAX], BUTTONS_ID_MAX);
289     if (!ac.check()) {
290         return ZX_ERR_NO_MEMORY;
291     }
292     size_t actual = 0;
293     status = device_get_metadata(parent_, DEVICE_METADATA_BUTTONS_BUTTONS, buttons.get(),
294                                  buttons.size() * sizeof(buttons_button_config_t), &actual);
295     if (status != ZX_OK) {
296         zxlogf(ERROR, "%s device_get_metadata failed %d\n", __FILE__, status);
297         return status;
298     }
299     size_t n_buttons = actual / sizeof(buttons_button_config_t);
300 
301     // We have up to BUTTONS_ID_MAX available gpios.
302     auto gpios_configs = fbl::Array(
303         new (&ac) buttons_gpio_config_t[BUTTONS_ID_MAX], BUTTONS_ID_MAX);
304     if (!ac.check()) {
305         return ZX_ERR_NO_MEMORY;
306     }
307     actual = 0;
308     status = device_get_metadata(parent_, DEVICE_METADATA_BUTTONS_GPIOS, gpios_configs.get(),
309                                  gpios_configs.size() * sizeof(buttons_gpio_config_t), &actual);
310     if (status != ZX_OK) {
311         zxlogf(ERROR, "%s device_get_metadata failed %d\n", __FILE__, status);
312         return status;
313     }
314     size_t n_gpios = actual / sizeof(buttons_gpio_config_t);
315 
316     buttons_ = fbl::Array(new (&ac) buttons_button_config_t[n_buttons], n_buttons);
317     if (!ac.check()) {
318         return ZX_ERR_NO_MEMORY;
319     }
320     gpios_ = fbl::Array(new (&ac) Gpio[n_gpios], n_gpios);
321     if (!ac.check()) {
322         return ZX_ERR_NO_MEMORY;
323     }
324 
325     for (uint32_t i = 0; i < buttons_.size(); ++i) {
326         buttons_[i] = buttons[i];
327         if (buttons_[i].gpioA_idx >= gpios_.size()) {
328             zxlogf(ERROR, "%s invalid gpioA_idx %u\n", __FUNCTION__, buttons_[i].gpioA_idx);
329             return ZX_ERR_INTERNAL;
330         }
331         if (buttons_[i].gpioB_idx >= gpios_.size()) {
332             zxlogf(ERROR, "%s invalid gpioB_idx %u\n", __FUNCTION__, buttons_[i].gpioB_idx);
333             return ZX_ERR_INTERNAL;
334         }
335         if (gpios_configs[buttons_[i].gpioA_idx].type != BUTTONS_GPIO_TYPE_INTERRUPT) {
336             zxlogf(ERROR, "%s invalid gpioA type %u\n", __FUNCTION__,
337                    gpios_configs[buttons_[i].gpioA_idx].type);
338             return ZX_ERR_INTERNAL;
339         }
340         if (buttons_[i].type == BUTTONS_TYPE_MATRIX &&
341             gpios_configs[buttons_[i].gpioB_idx].type != BUTTONS_GPIO_TYPE_MATRIX_OUTPUT) {
342             zxlogf(ERROR, "%s invalid matrix gpioB type %u\n", __FUNCTION__,
343                    gpios_configs[buttons_[i].gpioB_idx].type);
344             return ZX_ERR_INTERNAL;
345         }
346         if (buttons_[i].id == BUTTONS_ID_FDR) {
347             fdr_gpio_ = buttons_[i].gpioA_idx;
348             zxlogf(INFO, "FDR (up and down buttons) setup to GPIO %u\n", *fdr_gpio_);
349         }
350     }
351 
352     for (uint32_t i = 0; i < gpios_.size(); ++i) {
353         gpios_[i].config = gpios_configs[i];
354         size_t actual;
355         status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, i, &gpios_[i].gpio,
356                                    sizeof(gpios_[i].gpio), &actual);
357         if (status != ZX_OK) {
358             zxlogf(ERROR, "%s pdev_get_protocol failed %d\n", __FUNCTION__, status);
359             return ZX_ERR_NOT_SUPPORTED;
360         }
361         status = gpio_set_alt_function(&gpios_[i].gpio, 0); // 0 means function GPIO.
362         if (status != ZX_OK) {
363             zxlogf(ERROR, "%s gpio_set_alt_function failed %d\n", __FUNCTION__, status);
364             return ZX_ERR_NOT_SUPPORTED;
365         }
366         if (gpios_[i].config.type == BUTTONS_GPIO_TYPE_MATRIX_OUTPUT) {
367             status = gpio_config_out(&gpios_[i].gpio, gpios_[i].config.output_value);
368             if (status != ZX_OK) {
369                 zxlogf(ERROR, "%s gpio_config_out failed %d\n", __FUNCTION__, status);
370                 return ZX_ERR_NOT_SUPPORTED;
371             }
372         } else if (gpios_[i].config.type == BUTTONS_GPIO_TYPE_INTERRUPT) {
373             status = gpio_config_in(&gpios_[i].gpio, gpios_[i].config.internal_pull);
374             if (status != ZX_OK) {
375                 zxlogf(ERROR, "%s gpio_config_in failed %d\n", __FUNCTION__, status);
376                 return ZX_ERR_NOT_SUPPORTED;
377             }
378             status = ConfigureInterrupt(i, PORT_KEY_INTERRUPT_START + i);
379             if (status != ZX_OK) {
380                 return status;
381             }
382         }
383     }
384 
385     auto f = [](void* arg) -> int { return reinterpret_cast<HidButtonsDevice*>(arg)->Thread(); };
386     int rc = thrd_create_with_name(&thread_, f, this, "hid-buttons-thread");
387     if (rc != thrd_success) {
388         return ZX_ERR_INTERNAL;
389     }
390 
391     status = DdkAdd("hid-buttons");
392     if (status != ZX_OK) {
393         zxlogf(ERROR, "%s DdkAdd failed %d\n", __FUNCTION__, status);
394         ShutDown();
395         return status;
396     }
397 
398     return ZX_OK;
399 }
400 
ShutDown()401 void HidButtonsDevice::ShutDown() {
402     zx_port_packet packet = {PORT_KEY_SHUTDOWN, ZX_PKT_TYPE_USER, ZX_OK, {}};
403     zx_status_t status = port_.queue(&packet);
404     ZX_ASSERT(status == ZX_OK);
405     thrd_join(thread_, NULL);
406     for (uint32_t i = 0; i < gpios_.size(); ++i) {
407         gpios_[i].irq.destroy();
408     }
409     fbl::AutoLock lock(&client_lock_);
410     client_.clear();
411 }
412 
DdkUnbind()413 void HidButtonsDevice::DdkUnbind() {
414     ShutDown();
415     DdkRemove();
416 }
417 
DdkRelease()418 void HidButtonsDevice::DdkRelease() {
419     delete this;
420 }
421 
422 } // namespace buttons
423 
hid_buttons_bind(void * ctx,zx_device_t * parent)424 extern "C" zx_status_t hid_buttons_bind(void* ctx, zx_device_t* parent) {
425     fbl::AllocChecker ac;
426     auto dev = fbl::make_unique_checked<buttons::HidButtonsDevice>(&ac, parent);
427     if (!ac.check()) {
428         return ZX_ERR_NO_MEMORY;
429     }
430     auto status = dev->Bind();
431     if (status == ZX_OK) {
432         // devmgr is now in charge of the memory for dev.
433         __UNUSED auto ptr = dev.release();
434     }
435     return status;
436 }
437