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