@@ -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+
80100bool 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 (×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+
177327void 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*/
269419IOReturn 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 */
0 commit comments