Skip to content

Commit 85341d1

Browse files
committed
Working interrupts, but bad coordinates
1 parent 61b094a commit 85341d1

File tree

3 files changed

+184
-32
lines changed

3 files changed

+184
-32
lines changed

VoodooI2CGoodix/VoodooI2CGoodixTouchDriver.cpp

Lines changed: 160 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,26 @@ static const struct goodix_chip_data *goodix_get_chip_data(UInt16 id)
7777
}
7878
};
7979

80+
/* Temp: Taken from the Linux kernel source */
81+
static inline uint16_t __get_unaligned_le16(const uint8_t *p) {
82+
return p[0] | p[1] << 8;
83+
}
84+
85+
static inline uint16_t get_unaligned_le16(const void *p) {
86+
return __get_unaligned_le16((const uint8_t *)p);
87+
}
88+
89+
/* This is supposed to be a sub for kstrtou16(), adapted from https://stackoverflow.com/a/20020795/1170723 */
90+
static inline bool str_to_uint16(const char *str, uint16_t *res) {
91+
char *end;
92+
long val = strtol(str, &end, 10);
93+
if (end == str || *end != '\0' || val < 0 || val >= 0x10000) {
94+
return false;
95+
}
96+
*res = (uint16_t)val;
97+
return true;
98+
}
99+
80100
bool VoodooI2CGoodixTouchDriver::init(OSDictionary *properties) {
81101
transducers = NULL;
82102
if (!super::init(properties)) {
@@ -144,11 +164,11 @@ bool VoodooI2CGoodixTouchDriver::start(IOService* provider) {
144164
}
145165

146166
// set interrupts AFTER device is initialised
147-
// interrupt_source = IOInterruptEventSource::interruptEventSource(this, OSMemberFunctionCast(IOInterruptEventAction, this, &VoodooI2CGoodixTouchDriver::interrupt_occurred), api, 0);
148-
// if (!interrupt_source) {
149-
// IOLog("%s::Could not get interrupt event source\n", getName());
150-
// goto start_exit;
151-
// }
167+
interrupt_source = IOInterruptEventSource::interruptEventSource(this, OSMemberFunctionCast(IOInterruptEventAction, this, &VoodooI2CGoodixTouchDriver::interrupt_occurred), api, 0);
168+
if (!interrupt_source) {
169+
IOLog("%s::Could not get interrupt event source\n", getName());
170+
goto start_exit;
171+
}
152172
publish_multitouch_interface();
153173
if (!init_device()) {
154174
IOLog("%s::Failed to init device\n", getName());
@@ -157,8 +177,8 @@ bool VoodooI2CGoodixTouchDriver::start(IOService* provider) {
157177
else {
158178
IOLog("%s::Device initialized\n", getName());
159179
}
160-
// workLoop->addEventSource(interrupt_source);
161-
// interrupt_source->enable();
180+
workLoop->addEventSource(interrupt_source);
181+
interrupt_source->enable();
162182
PMinit();
163183
api->joinPMtree(this);
164184
registerPowerDriver(this, VoodooI2CIOPMPowerStates, kVoodooI2CIOPMNumberPowerStates);
@@ -174,6 +194,136 @@ bool VoodooI2CGoodixTouchDriver::start(IOService* provider) {
174194
return false;
175195
}
176196

197+
void VoodooI2CGoodixTouchDriver::interrupt_occurred(OSObject* owner, IOInterruptEventSource* src, int intCount) {
198+
if (read_in_progress)
199+
return;
200+
if (!awake)
201+
return;
202+
read_in_progress = true;
203+
thread_t new_thread;
204+
kern_return_t ret = kernel_thread_start(OSMemberFunctionCast(thread_continue_t, this, &VoodooI2CGoodixTouchDriver::handle_input_threaded), this, &new_thread);
205+
if (ret != KERN_SUCCESS) {
206+
read_in_progress = false;
207+
IOLog("%s::Thread error while attemping to get input report\n", getName());
208+
} else {
209+
thread_deallocate(new_thread);
210+
}
211+
}
212+
213+
void VoodooI2CGoodixTouchDriver::handle_input_threaded() {
214+
if (!ready_for_input) {
215+
read_in_progress = false;
216+
return;
217+
}
218+
command_gate->attemptAction(OSMemberFunctionCast(IOCommandGate::Action, this, &VoodooI2CGoodixTouchDriver::goodix_process_events));
219+
read_in_progress = false;
220+
}
221+
222+
void VoodooI2CGoodixTouchDriver::goodix_process_events(struct goodix_ts_data *ts) {
223+
UInt8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
224+
225+
int touch_num;
226+
int i;
227+
228+
touch_num = goodix_ts_read_input_report(ts, point_data);
229+
if (touch_num < 0) {
230+
return;
231+
}
232+
233+
IOLog("%s::Got %d touches!\n", getName(), touch_num);
234+
235+
/*
236+
* Bit 4 of the first byte reports the status of the capacitive
237+
* Windows/Home button.
238+
*/
239+
// input_report_key(ts->input_dev, KEY_LEFTMETA, point_data[0] & BIT(4));
240+
241+
for (i = 0; i < touch_num; i++) {
242+
goodix_ts_report_touch(ts, &point_data[1 + GOODIX_CONTACT_SIZE * i]);
243+
}
244+
}
245+
246+
int VoodooI2CGoodixTouchDriver::goodix_ts_read_input_report(struct goodix_ts_data *ts, UInt8 *data) {
247+
248+
uint64_t max_timeout;
249+
int touch_num;
250+
IOReturn retVal;
251+
252+
AbsoluteTime timestamp;
253+
uint64_t timestamp_ns;
254+
clock_get_uptime(&timestamp);
255+
absolutetime_to_nanoseconds(timestamp, &timestamp_ns);
256+
257+
/*
258+
* The 'buffer status' bit, which indicates that the data is valid, is
259+
* not set as soon as the interrupt is raised, but slightly after.
260+
* This takes around 10 ms to happen, so we poll for GOODIX_BUFFER_STATUS_TIMEOUT (20ms).
261+
*/
262+
max_timeout = timestamp_ns + GOODIX_BUFFER_STATUS_TIMEOUT;
263+
do {
264+
clock_get_uptime(&timestamp);
265+
absolutetime_to_nanoseconds(timestamp, &timestamp_ns);
266+
267+
retVal = goodix_read_reg(GOODIX_READ_COOR_ADDR, data, GOODIX_CONTACT_SIZE + 1);
268+
if (retVal != kIOReturnSuccess) {
269+
IOLog("%s::I2C transfer starting coordinate read: %d\n", getName(), retVal);
270+
return -1;
271+
}
272+
if (data[0] & GOODIX_BUFFER_STATUS_READY) {
273+
touch_num = data[0] & 0x0f;
274+
/*
275+
if (touch_num > ts->max_touch_num) {
276+
IOLog("%s::Error: got more touches than we should have (got %d, max = %d)\n", getName(), touch_num, ts->max_touch_num);
277+
return -1;
278+
}
279+
*/
280+
281+
if (touch_num > 1) {
282+
data += 1 + GOODIX_CONTACT_SIZE;
283+
retVal = goodix_read_reg(GOODIX_READ_COOR_ADDR + 1 + GOODIX_CONTACT_SIZE, data, GOODIX_CONTACT_SIZE * (touch_num - 1));
284+
if (retVal != kIOReturnSuccess) {
285+
IOLog("%s::I2C transfer error during coordinate read: %d\n", getName(), retVal);
286+
return -1;
287+
}
288+
}
289+
290+
return touch_num;
291+
}
292+
293+
usleep_range(1000, 2000); /* Poll every 1 - 2 ms */
294+
} while (timestamp_ns < max_timeout);
295+
296+
/*
297+
* The Goodix panel will send spurious interrupts after a
298+
* 'finger up' event, which will always cause a timeout.
299+
*/
300+
return 0;
301+
}
302+
303+
void VoodooI2CGoodixTouchDriver::goodix_ts_report_touch(struct goodix_ts_data *ts, UInt8 *coor_data) {
304+
int id = coor_data[0] & 0x0F;
305+
int input_x = get_unaligned_le16(&coor_data[1]);
306+
int input_y = get_unaligned_le16(&coor_data[3]);
307+
int input_w = get_unaligned_le16(&coor_data[5]);
308+
309+
/* Inversions have to happen before axis swapping */
310+
// if (ts->inverted_x)
311+
// input_x = ts->abs_x_max - input_x;
312+
// if (ts->inverted_y)
313+
// input_y = ts->abs_y_max - input_y;
314+
// if (ts->swapped_x_y)
315+
// swap(input_x, input_y);
316+
317+
// input_mt_slot(ts->input_dev, id);
318+
// input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
319+
// input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
320+
// input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
321+
// input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
322+
// input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
323+
324+
IOLog("%s::Touch at %d, %d with width %d\n", getName(), input_x, input_y, input_w);
325+
}
326+
177327
void VoodooI2CGoodixTouchDriver::stop(IOService* provider) {
178328
release_resources();
179329
unpublish_multitouch_interface();
@@ -267,32 +417,12 @@ void VoodooI2CGoodixTouchDriver::release_resources() {
267417

268418
/* Adapted from the TFE Driver*/
269419
IOReturn VoodooI2CGoodixTouchDriver::goodix_read_reg(UInt16 reg, UInt8* values, size_t len) {
270-
IOReturn relVal = kIOReturnSuccess;
420+
IOReturn retVal = kIOReturnSuccess;
271421
UInt16 buffer[] {
272422
OSSwapHostToBigInt16(reg)
273423
};
274-
relVal = api->writeReadI2C(reinterpret_cast<UInt8*>(&buffer), sizeof(buffer), values, len);
275-
return relVal;
276-
}
277-
278-
/* Temp: Taken from the Linux kernel source */
279-
static inline uint16_t __get_unaligned_le16(const uint8_t *p) {
280-
return p[0] | p[1] << 8;
281-
}
282-
283-
static inline uint16_t get_unaligned_le16(const void *p) {
284-
return __get_unaligned_le16((const uint8_t *)p);
285-
}
286-
287-
/* This is supposed to be a sub for kstrtou16(), adapted from https://stackoverflow.com/a/20020795/1170723 */
288-
static inline bool str_to_uint16(const char *str, uint16_t *res) {
289-
char *end;
290-
long val = strtol(str, &end, 10);
291-
if (end == str || *end != '\0' || val < 0 || val >= 0x10000) {
292-
return false;
293-
}
294-
*res = (uint16_t)val;
295-
return true;
424+
retVal = api->writeReadI2C(reinterpret_cast<UInt8*>(&buffer), sizeof(buffer), values, len);
425+
return retVal;
296426
}
297427

298428
/* Ported from goodix.c */

VoodooI2CGoodix/VoodooI2CGoodixTouchDriver.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,27 @@ class VoodooI2CGoodixTouchDriver : public IOService {
109109
* @ts: our goodix_ts_data pointer
110110
*/
111111
void goodix_read_config(struct goodix_ts_data *ts);
112+
113+
/* Handles any interrupts that the Goodix device generates
114+
* by spawning a thread that is out of the interrupt context
115+
*/
116+
void interrupt_occurred(OSObject* owner, IOInterruptEventSource* src, int intCount);
117+
118+
/* Handles input in a threaded manner, then
119+
* calls parse_goodix_report via the command gate for synchronisation
120+
*/
121+
void handle_input_threaded();
122+
123+
/* Process incoming events. Called when the IRQ is triggered.
124+
* Read the current device state, and push the input events to the user space.
125+
*
126+
* @ts: our goodix_ts_data pointer
127+
*/
128+
void goodix_process_events(struct goodix_ts_data *ts);
129+
130+
void goodix_ts_report_touch(struct goodix_ts_data *ts, UInt8 *coor_data);
131+
132+
int goodix_ts_read_input_report(struct goodix_ts_data *ts, UInt8 *data);
112133
};
113134

114135
#endif /* VoodooI2CGoodixTouchDriver_hpp */

VoodooI2CGoodix/goodix.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#ifndef VoodooI2CGoodix_h
22
#define VoodooI2CGoodix_h
33

4-
#define GOODIX_MAX_CONTACTS 10
5-
64
#define GOODIX_READ_COOR_ADDR 0x814E
75
#define GOODIX_GT1X_REG_CONFIG_DATA 0x8050
86
#define GOODIX_GT9X_REG_CONFIG_DATA 0x8047
@@ -22,6 +20,9 @@
2220
#define MAX_CONTACTS_LOC 5
2321
#define TRIGGER_LOC 6
2422

23+
#define GOODIX_BUFFER_STATUS_READY BIT(7)
24+
#define GOODIX_BUFFER_STATUS_TIMEOUT 20000000
25+
2526
#define msleep(x) IOSleep(x)
2627
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
2728
#define usleep_range(min, max) msleep(DIV_ROUND_UP(min, 1000))

0 commit comments

Comments
 (0)