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 <ddk/debug.h>
6 #include <ddk/protocol/platform/device.h>
7 #include <ddk/protocol/i2c-lib.h>
8 #include <fbl/algorithm.h>
9 #include <fbl/auto_call.h>
10 #include <fbl/auto_lock.h>
11 #include <fbl/ref_counted.h>
12 #include <fbl/ref_ptr.h>
13 #include <hw/arch_ops.h>
14 #include <hw/reg.h>
15 #include <zircon/compiler.h>
16 
17 #include <stdio.h>
18 #include <string.h>
19 
20 #include "ft3x27.h"
21 
22 namespace ft {
23 
Ft3x27Device(zx_device_t * device)24 Ft3x27Device::Ft3x27Device(zx_device_t* device)
25     : ddk::Device<Ft3x27Device, ddk::Unbindable>(device) {
26 }
27 
ParseReport(ft3x27_finger_t * rpt,uint8_t * buf)28 void Ft3x27Device::ParseReport(ft3x27_finger_t* rpt, uint8_t* buf) {
29     rpt->x = static_cast<uint16_t>(((buf[0] & 0x0f) << 8) + buf[1]);
30     rpt->y = static_cast<uint16_t>(((buf[2] & 0x0f) << 8) + buf[3]);
31     rpt->finger_id = static_cast<uint8_t>(
32         ((buf[2] >> 2) & FT3X27_FINGER_ID_CONTACT_MASK) |
33         (((buf[0] & 0xC0) == 0x80) ? 1 : 0));
34 }
35 
Thread()36 int Ft3x27Device::Thread() {
37     zx_status_t status;
38     zxlogf(INFO, "ft3x27: entering irq thread\n");
39     while (true) {
40         status = irq_.wait(nullptr);
41         if (!running_.load()) {
42             return ZX_OK;
43         }
44         if (status != ZX_OK) {
45             zxlogf(ERROR, "ft3x27: Interrupt error %d\n", status);
46         }
47         uint8_t i2c_buf[kMaxPoints * kFingerRptSize + 1];
48         status = Read(FTS_REG_CURPOINT, i2c_buf, kMaxPoints * kFingerRptSize + 1);
49         if (status == ZX_OK) {
50             fbl::AutoLock lock(&client_lock_);
51             ft_rpt_.rpt_id = FT3X27_RPT_ID_TOUCH;
52             ft_rpt_.contact_count = i2c_buf[0];
53             for (uint i = 0; i < kMaxPoints; i++) {
54                 ParseReport(&ft_rpt_.fingers[i], &i2c_buf[i * kFingerRptSize + 1]);
55             }
56             if (client_.is_valid()) {
57                 client_.IoQueue(reinterpret_cast<uint8_t*>(&ft_rpt_), sizeof(ft3x27_touch_t));
58             }
59         } else {
60             zxlogf(ERROR, "ft3x27: i2c read error\n");
61         }
62     }
63     zxlogf(INFO, "ft3x27: exiting\n");
64 }
65 
InitPdev()66 zx_status_t Ft3x27Device::InitPdev() {
67     pdev_protocol_t pdev;
68 
69     zx_status_t status = device_get_protocol(parent_, ZX_PROTOCOL_PDEV, &pdev);
70     if (status != ZX_OK) {
71         zxlogf(ERROR, "ft3x27: failed to acquire pdev\n");
72         return status;
73     }
74 
75     status = device_get_protocol(parent_, ZX_PROTOCOL_I2C, &i2c_);
76     if (status != ZX_OK) {
77         zxlogf(ERROR, "ft3x27: failed to acquire i2c\n");
78         return status;
79     }
80 
81     for (uint32_t i = 0; i < FT_PIN_COUNT; i++) {
82         size_t actual;
83         status = pdev_get_protocol(&pdev, ZX_PROTOCOL_GPIO, i, &gpios_[i], sizeof(gpios_[i]),
84                                    &actual);
85         if (status != ZX_OK) {
86             return status;
87         }
88     }
89 
90     gpio_config_in(&gpios_[FT_INT_PIN], GPIO_NO_PULL);
91 
92     status = gpio_get_interrupt(&gpios_[FT_INT_PIN],
93                                 ZX_INTERRUPT_MODE_EDGE_LOW,
94                                 irq_.reset_and_get_address());
95     if (status != ZX_OK) {
96         return status;
97     }
98 
99     return ZX_OK;
100 }
101 
Create(zx_device_t * device)102 zx_status_t Ft3x27Device::Create(zx_device_t* device) {
103 
104     zxlogf(INFO, "ft3x27: driver started...\n");
105 
106     auto ft_dev = fbl::make_unique<Ft3x27Device>(device);
107     zx_status_t status = ft_dev->InitPdev();
108     if (status != ZX_OK) {
109         zxlogf(ERROR, "ft3x27: Driver bind failed %d\n", status);
110         return status;
111     }
112 
113     auto thunk = [](void* arg) -> int {
114         return reinterpret_cast<Ft3x27Device*>(arg)->Thread();
115     };
116 
117     auto cleanup = fbl::MakeAutoCall([&]() { ft_dev->ShutDown(); });
118 
119     ft_dev->running_.store(true);
120     int ret = thrd_create_with_name(&ft_dev->thread_, thunk,
121                                     reinterpret_cast<void*>(ft_dev.get()),
122                                     "ft3x27-thread");
123     ZX_DEBUG_ASSERT(ret == thrd_success);
124 
125     status = ft_dev->DdkAdd("ft3x27 HidDevice");
126     if (status != ZX_OK) {
127         zxlogf(ERROR, "ft3x27: Could not create hid device: %d\n", status);
128         return status;
129     } else {
130         zxlogf(INFO, "ft3x27: Added hid device\n");
131     }
132 
133     cleanup.cancel();
134 
135     // device intentionally leaked as it is now held by DevMgr
136     __UNUSED auto ptr = ft_dev.release();
137 
138     return ZX_OK;
139 }
140 
HidbusQuery(uint32_t options,hid_info_t * info)141 zx_status_t Ft3x27Device::HidbusQuery(uint32_t options, hid_info_t* info) {
142     if (!info) {
143         return ZX_ERR_INVALID_ARGS;
144     }
145     info->dev_num = 0;
146     info->device_class = HID_DEVICE_CLASS_OTHER;
147     info->boot_device = false;
148 
149     return ZX_OK;
150 }
151 
DdkRelease()152 void Ft3x27Device::DdkRelease() {
153     delete this;
154 }
155 
DdkUnbind()156 void Ft3x27Device::DdkUnbind() {
157     ShutDown();
158     DdkRemove();
159 }
160 
ShutDown()161 zx_status_t Ft3x27Device::ShutDown() {
162     running_.store(false);
163     irq_.destroy();
164     thrd_join(thread_, NULL);
165     {
166         fbl::AutoLock lock(&client_lock_);
167         //client_.clear();
168     }
169     return ZX_OK;
170 }
171 
HidbusGetDescriptor(uint8_t desc_type,void ** data,size_t * len)172 zx_status_t Ft3x27Device::HidbusGetDescriptor(uint8_t desc_type, void** data, size_t* len) {
173 
174     const uint8_t* desc_ptr;
175     uint8_t* buf;
176     *len = get_ft3x27_report_desc(&desc_ptr);
177     fbl::AllocChecker ac;
178     buf = new (&ac) uint8_t[*len];
179     if (!ac.check()) {
180         return ZX_ERR_NO_MEMORY;
181     }
182     memcpy(buf, desc_ptr, *len);
183     *data = buf;
184     return ZX_OK;
185 }
186 
HidbusGetReport(uint8_t rpt_type,uint8_t rpt_id,void * data,size_t len,size_t * out_len)187 zx_status_t Ft3x27Device::HidbusGetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
188                                           size_t len, size_t* out_len) {
189     return ZX_ERR_NOT_SUPPORTED;
190 }
191 
HidbusSetReport(uint8_t rpt_type,uint8_t rpt_id,const void * data,size_t len)192 zx_status_t Ft3x27Device::HidbusSetReport(uint8_t rpt_type, uint8_t rpt_id, const void* data,
193                                           size_t len) {
194     return ZX_ERR_NOT_SUPPORTED;
195 }
196 
HidbusGetIdle(uint8_t rpt_id,uint8_t * duration)197 zx_status_t Ft3x27Device::HidbusGetIdle(uint8_t rpt_id, uint8_t* duration) {
198     return ZX_ERR_NOT_SUPPORTED;
199 }
200 
HidbusSetIdle(uint8_t rpt_id,uint8_t duration)201 zx_status_t Ft3x27Device::HidbusSetIdle(uint8_t rpt_id, uint8_t duration) {
202     return ZX_ERR_NOT_SUPPORTED;
203 }
204 
HidbusGetProtocol(uint8_t * protocol)205 zx_status_t Ft3x27Device::HidbusGetProtocol(uint8_t* protocol) {
206     return ZX_ERR_NOT_SUPPORTED;
207 }
208 
HidbusSetProtocol(uint8_t protocol)209 zx_status_t Ft3x27Device::HidbusSetProtocol(uint8_t protocol) {
210     return ZX_OK;
211 }
212 
HidbusStop()213 void Ft3x27Device::HidbusStop() {
214     fbl::AutoLock lock(&client_lock_);
215     client_.clear();
216 }
217 
HidbusStart(const hidbus_ifc_t * ifc)218 zx_status_t Ft3x27Device::HidbusStart(const hidbus_ifc_t* ifc) {
219     fbl::AutoLock lock(&client_lock_);
220     if (client_.is_valid()) {
221         zxlogf(ERROR, "ft3x27: Already bound!\n");
222         return ZX_ERR_ALREADY_BOUND;
223     } else {
224         client_ = ddk::HidbusIfcClient(ifc);
225         zxlogf(INFO, "ft3x27: started\n");
226     }
227     return ZX_OK;
228 }
229 
230 // simple i2c read for reading one register location
231 //  intended mostly for debug purposes
Read(uint8_t addr)232 uint8_t Ft3x27Device::Read(uint8_t addr) {
233     uint8_t rbuf;
234     i2c_write_read_sync(&i2c_, &addr, 1, &rbuf, 1);
235     return rbuf;
236 }
237 
Read(uint8_t addr,uint8_t * buf,size_t len)238 zx_status_t Ft3x27Device::Read(uint8_t addr, uint8_t* buf, size_t len) {
239     // TODO(bradenkell): Remove this workaround when transfers of more than 8 bytes are supported on
240     // the MT8167.
241     while (len > 0) {
242         size_t readlen = fbl::min(len, kMaxI2cTransferLength);
243 
244         zx_status_t status = i2c_write_read_sync(&i2c_, &addr, 1, buf, readlen);
245         if (status != ZX_OK) {
246             zxlogf(ERROR, "Failed to read i2c - %d\n", status);
247             return status;
248         }
249 
250         addr = static_cast<uint8_t>(addr + readlen);
251         buf += readlen;
252         len -= readlen;
253     }
254 
255     return ZX_OK;
256 }
257 } //namespace ft
258 
ft3x27_bind(void * ctx,zx_device_t * device,void ** cookie)259 extern "C" zx_status_t ft3x27_bind(void* ctx, zx_device_t* device, void** cookie) {
260     return ft::Ft3x27Device::Create(device);
261 }
262