@@ -77,6 +77,26 @@ static const struct goodix_chip_data *goodix_get_chip_data(UInt16 id)
77
77
}
78
78
};
79
79
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
+
80
100
bool VoodooI2CGoodixTouchDriver::init (OSDictionary *properties) {
81
101
transducers = NULL ;
82
102
if (!super::init (properties)) {
@@ -144,11 +164,11 @@ bool VoodooI2CGoodixTouchDriver::start(IOService* provider) {
144
164
}
145
165
146
166
// 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
+ }
152
172
publish_multitouch_interface ();
153
173
if (!init_device ()) {
154
174
IOLog (" %s::Failed to init device\n " , getName ());
@@ -157,8 +177,8 @@ bool VoodooI2CGoodixTouchDriver::start(IOService* provider) {
157
177
else {
158
178
IOLog (" %s::Device initialized\n " , getName ());
159
179
}
160
- // workLoop->addEventSource(interrupt_source);
161
- // interrupt_source->enable();
180
+ workLoop->addEventSource (interrupt_source);
181
+ interrupt_source->enable ();
162
182
PMinit ();
163
183
api->joinPMtree (this );
164
184
registerPowerDriver (this , VoodooI2CIOPMPowerStates, kVoodooI2CIOPMNumberPowerStates );
@@ -174,6 +194,136 @@ bool VoodooI2CGoodixTouchDriver::start(IOService* provider) {
174
194
return false ;
175
195
}
176
196
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 (×tamp);
255
+ absolutetime_to_nanoseconds (timestamp, ×tamp_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 (×tamp);
265
+ absolutetime_to_nanoseconds (timestamp, ×tamp_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
+
177
327
void VoodooI2CGoodixTouchDriver::stop (IOService* provider) {
178
328
release_resources ();
179
329
unpublish_multitouch_interface ();
@@ -267,32 +417,12 @@ void VoodooI2CGoodixTouchDriver::release_resources() {
267
417
268
418
/* Adapted from the TFE Driver*/
269
419
IOReturn VoodooI2CGoodixTouchDriver::goodix_read_reg (UInt16 reg, UInt8 * values, size_t len) {
270
- IOReturn relVal = kIOReturnSuccess ;
420
+ IOReturn retVal = kIOReturnSuccess ;
271
421
UInt16 buffer[] {
272
422
OSSwapHostToBigInt16 (reg)
273
423
};
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;
296
426
}
297
427
298
428
/* Ported from goodix.c */
0 commit comments