1 /*
2 * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com.
3 * Copyright 2008-2011, Michael Lotz <mmlr@mlotz.ch>
4 * Copyright 2020, 2022 Vladimir Kondratyev <wulf@FreeBSD.org>
5 * Copyright 2023 Vladimir Serbinenko <phcoder@gmail.com>
6 * Distributed under the terms of the MIT license.
7 */
8
9
10 //! Driver for I2C Elan Devices.
11 // Partially based on FreeBSD ietp driver
12
13
14 #include "Driver.h"
15 #include "ELANDevice.h"
16 #include "HIDReport.h"
17 #include <keyboard_mouse_driver.h>
18 #include <kernel.h>
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <new>
25
26 #define LO_ELAN_REPORT_SIZE 32
27 #define HI_ELAN_REPORT_SIZE 37
28 #define MIN_ELAN_REPORT 11
29
30 #define IETP_MAX_X_AXIS 0x0106
31 #define IETP_MAX_Y_AXIS 0x0107
32
33 #define IETP_CONTROL 0x0300
34 #define IETP_CTRL_ABSOLUTE 0x0001
35 #define IETP_CTRL_STANDARD 0x0000
36
ELANDevice(device_node * parent,i2c_device_interface * i2c,i2c_device i2cCookie)37 ELANDevice::ELANDevice(device_node* parent, i2c_device_interface* i2c,
38 i2c_device i2cCookie)
39 : fStatus(B_NO_INIT),
40 fTransferLastschedule(0),
41 fTransferScheduled(0),
42 fOpenCount(0),
43 fRemoved(false),
44 fPublishPath(nullptr),
45 fReportID(0x5d),
46 fHighPrecision(false),
47 fParent(parent),
48 fI2C(i2c),
49 fI2CCookie(i2cCookie),
50 fLastButtons(0),
51 fClickCount(0),
52 fLastClickTime(0),
53 fClickSpeed(250000),
54 fReportStatus(B_NO_INIT),
55 fCurrentReportLength(0),
56 fBusyCount(0)
57 {
58 fConditionVariable.Init(this, "elan report");
59
60 uint16 descriptorAddress = 1;
61 // fetch HID descriptor
62 CALLED();
63 fStatus = _FetchBuffer((uint8*)&descriptorAddress,
64 sizeof(descriptorAddress), &fDescriptor, sizeof(fDescriptor));
65 if (fStatus != B_OK) {
66 ERROR("failed to fetch HID descriptor\n");
67 return;
68 }
69
70 if (_SetAbsoluteMode(true) != B_OK) {
71 TRACE_ALWAYS("failed to set absolute mode\n");
72 return;
73 }
74
75 fHardwareSpecs.areaStartX = 0;
76 fHardwareSpecs.areaStartY = 0;
77
78 uint16_t buf;
79
80 if (_ReadRegister(IETP_MAX_X_AXIS, sizeof(buf), &buf) != B_OK) {
81 TRACE_ALWAYS("failed reading max x\n");
82 return;
83 }
84 fHardwareSpecs.areaEndX = buf;
85
86 if (_ReadRegister(IETP_MAX_Y_AXIS, sizeof(buf), &buf) != B_OK) {
87 TRACE_ALWAYS("failed reading max y\n");
88 return;
89 }
90 fHardwareSpecs.areaEndY = buf;
91
92 fHardwareSpecs.edgeMotionWidth = 55;
93
94 fHardwareSpecs.minPressure = 0;
95 fHardwareSpecs.realMaxPressure = 50;
96 fHardwareSpecs.maxPressure = 255;
97
98 TRACE("Dimensions %dx%d\n", fHardwareSpecs.areaEndX, fHardwareSpecs.areaEndY);
99
100 fStatus = B_OK;
101 }
102
103
~ELANDevice()104 ELANDevice::~ELANDevice()
105 {
106 }
107
108
109 status_t
Open(uint32 flags)110 ELANDevice::Open(uint32 flags)
111 {
112 atomic_add(&fOpenCount, 1);
113 _Reset();
114
115 return B_OK;
116 }
117
118
119 status_t
Close()120 ELANDevice::Close()
121 {
122 atomic_add(&fOpenCount, -1);
123 _SetPower(I2C_HID_POWER_OFF);
124
125 return B_OK;
126 }
127
128
129 void
Removed()130 ELANDevice::Removed()
131 {
132 fRemoved = true;
133 }
134
135
136 status_t
_MaybeScheduleTransfer(int type,int id,int reportSize)137 ELANDevice::_MaybeScheduleTransfer(int type, int id, int reportSize)
138 {
139 if (fRemoved)
140 return ENODEV;
141
142 if (atomic_get_and_set(&fTransferScheduled, 1) != 0) {
143 // someone else already caused a transfer to be scheduled
144 return B_OK;
145 }
146
147 snooze_until(fTransferLastschedule, B_SYSTEM_TIMEBASE);
148 fTransferLastschedule = system_time() + 10000;
149
150 TRACE("scheduling interrupt transfer of %u bytes\n",
151 reportSize);
152 return _FetchReport(type, id, reportSize);
153 }
154
155 void
SetPublishPath(char * publishPath)156 ELANDevice::SetPublishPath(char *publishPath)
157 {
158 free(fPublishPath);
159 fPublishPath = publishPath;
160 }
161
162 status_t
Control(uint32 op,void * buffer,size_t length)163 ELANDevice::Control(uint32 op, void *buffer,
164 size_t length)
165 {
166 switch (op) {
167
168 case B_GET_DEVICE_NAME:
169 {
170 if (!IS_USER_ADDRESS(buffer))
171 return B_BAD_ADDRESS;
172
173 if (user_strlcpy((char *)buffer, "Elantech I2C touchpad", length) > 0)
174 return B_OK;
175
176 return B_ERROR;
177 }
178
179 case MS_IS_TOUCHPAD:
180 TRACE("ELANTECH: MS_IS_TOUCHPAD\n");
181 if (buffer == NULL)
182 return B_OK;
183 return user_memcpy(buffer, &fHardwareSpecs, sizeof(fHardwareSpecs));
184
185 case MS_READ_TOUCHPAD:
186 {
187 touchpad_read read;
188 int zero_report_count = 0;
189 if (length < sizeof(touchpad_read))
190 return B_BUFFER_OVERFLOW;
191
192 if (user_memcpy(&read.timeout, &(((touchpad_read*)buffer)->timeout),
193 sizeof(bigtime_t)) != B_OK)
194 return B_BAD_ADDRESS;
195
196 read.event = MS_READ_TOUCHPAD;
197
198 while (true) {
199 status_t result = _ReadAndParseReport(
200 &read.u.touchpad, read.timeout,
201 zero_report_count);
202 if (result == B_INTERRUPTED)
203 continue;
204
205 if (!IS_USER_ADDRESS(buffer)
206 || user_memcpy(buffer, &read, sizeof(read))
207 != B_OK) {
208 return B_BAD_ADDRESS;
209 }
210
211 TRACE("Returning MS_READ_TOUCHPAD: %x\n", result);
212
213 return result;
214 }
215 }
216 }
217
218 return B_ERROR;
219 }
220
221
222 status_t
_SetAbsoluteMode(bool enable)223 ELANDevice::_SetAbsoluteMode(bool enable)
224 {
225 return _WriteRegister(IETP_CONTROL, enable ? IETP_CTRL_ABSOLUTE : IETP_CTRL_STANDARD);
226 }
227
228
229 status_t
_WaitForReport(bigtime_t timeout)230 ELANDevice::_WaitForReport(bigtime_t timeout)
231 {
232 CALLED();
233 while (atomic_get(&fBusyCount) != 0)
234 snooze(1000);
235
236 ConditionVariableEntry conditionVariableEntry;
237 fConditionVariable.Add(&conditionVariableEntry);
238 TRACE("Starting report wait\n");
239 status_t result = _MaybeScheduleTransfer(
240 HID_REPORT_TYPE_INPUT, fReportID,
241 fHighPrecision ? HI_ELAN_REPORT_SIZE : LO_ELAN_REPORT_SIZE);
242 if (result != B_OK) {
243 TRACE_ALWAYS("scheduling transfer failed\n");
244 conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, 0);
245 return result;
246 }
247
248 result = conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, timeout);
249 if (result != B_OK)
250 return result;
251
252 if (fReportStatus != B_OK)
253 return fReportStatus;
254
255 atomic_add(&fBusyCount, 1);
256 return B_OK;
257 }
258
259
260 status_t
_ReadAndParseReport(touchpad_movement * info,bigtime_t timeout,int & zero_report_count)261 ELANDevice::_ReadAndParseReport(touchpad_movement *info, bigtime_t timeout, int &zero_report_count)
262 {
263 CALLED();
264 status_t result = _WaitForReport(timeout);
265 if (result != B_OK) {
266 if (IsRemoved()) {
267 TRACE("device has been removed\n");
268 return B_DEV_NOT_READY;
269 }
270
271 if (result != B_INTERRUPTED) {
272 // interrupts happen when other reports come in on the same
273 // input as ours
274 TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
275 }
276
277 if (result == B_TIMED_OUT) {
278 return result;
279 }
280
281 // signal that we simply want to try again
282 return B_INTERRUPTED;
283 }
284
285 if (fCurrentReportLength == 0 && ++zero_report_count < 3) {
286 atomic_add(&fBusyCount, -1);
287 return B_INTERRUPTED;
288 }
289
290 uint8 report_copy[TRANSFER_BUFFER_SIZE];
291 memset(report_copy, 0, TRANSFER_BUFFER_SIZE);
292 memcpy(report_copy, fCurrentReport, MIN(TRANSFER_BUFFER_SIZE, fCurrentReportLength));
293 atomic_add(&fBusyCount, -1);
294
295 memset(info, 0, sizeof(*info));
296
297 if (report_copy[0] == 0 || report_copy[0] == 0xff)
298 return B_OK;
299
300 info->buttons = report_copy[0] & 0x7;
301 uint8 fingers = (report_copy[0] >> 3) & 0x1f;
302 info->fingers = fingers;
303 TRACE("buttons=%x, fingers=%x\n", info->buttons, fingers);
304 const uint8 *fingerData = fCurrentReport + 1;
305 int fingerCount = 0;
306 int sumx = 0, sumy = 0, sumz = 0, sumw = 0;
307 for (int finger = 0; finger < 5; finger++, fingerData += 5)
308 if (fingers & (1 << finger)) {
309 TRACE("finger %d:\n", finger);
310 uint8 wh;
311 int x, y, w;
312 if (fHighPrecision) {
313 x = fingerData[0] << 8 | fingerData[1];
314 y = fingerData[2] << 8 | fingerData[3];
315 wh = report_copy[30 + finger];
316 } else {
317 x = (fingerData[0] & 0xf0) << 4 | fingerData[1];
318 y = (fingerData[0] & 0x0f) << 8 | fingerData[2];
319 wh = fingerData[3];
320 }
321
322 int z = fingerData[4];
323
324 w = MAX((wh >> 4) & 0xf, wh & 0xf);
325
326 sumw += w;
327 sumx += x;
328 sumy += y;
329 sumz += z;
330
331 TRACE("x=%d, y=%d, z=%d, w=%d, wh=0x%x\n", x, y, z, w, wh);
332
333 fingerCount++;
334 }
335 if (fingerCount > 0) {
336 info->xPosition = sumx / fingerCount;
337 info->yPosition = sumy / fingerCount;
338 info->zPressure = sumz / fingerCount;
339 info->fingerWidth = MIN(12, sumw / fingerCount);
340 }
341
342 TRACE("Resulting position=[%d, %d, %d, %d]\n",
343 info->xPosition, info->yPosition, info->zPressure,
344 info->fingerWidth);
345
346 return B_OK;
347 }
348
349
350 void
_UnstallCallback(void * cookie,status_t status,void * data,size_t actualLength)351 ELANDevice::_UnstallCallback(void *cookie, status_t status, void *data,
352 size_t actualLength)
353 {
354 ELANDevice *device = (ELANDevice *)cookie;
355 if (status != B_OK) {
356 TRACE_ALWAYS("Unable to unstall device: %s\n", strerror(status));
357 }
358
359 // Now report the original failure, since we're ready to retry
360 _TransferCallback(cookie, B_ERROR, device->fTransferBuffer, 0);
361 }
362
363
364 void
_TransferCallback(void * cookie,status_t status,void * data,size_t actualLength)365 ELANDevice::_TransferCallback(void *cookie, status_t status, void *data,
366 size_t actualLength)
367 {
368 ELANDevice *device = (ELANDevice *)cookie;
369
370 atomic_set(&device->fTransferScheduled, 0);
371 device->_SetReport(status, device->fTransferBuffer, actualLength);
372 }
373
374
375 status_t
_Reset()376 ELANDevice::_Reset()
377 {
378 CALLED();
379 status_t status = _SetPower(I2C_HID_POWER_ON);
380 if (status != B_OK)
381 return status;
382
383 snooze(1000);
384
385 uint8 cmd[] = {
386 (uint8)(fDescriptor.wCommandRegister & 0xff),
387 (uint8)(fDescriptor.wCommandRegister >> 8),
388 0,
389 I2C_HID_CMD_RESET,
390 };
391
392 status = _ExecCommand(I2C_OP_WRITE_STOP, cmd, sizeof(cmd), NULL, 0);
393 if (status != B_OK) {
394 _SetPower(I2C_HID_POWER_OFF);
395 return status;
396 }
397
398 snooze(1000);
399 return B_OK;
400 }
401
402
403 status_t
_SetPower(uint8 power)404 ELANDevice::_SetPower(uint8 power)
405 {
406 CALLED();
407 uint8 cmd[] = {
408 (uint8)(fDescriptor.wCommandRegister & 0xff),
409 (uint8)(fDescriptor.wCommandRegister >> 8),
410 power,
411 I2C_HID_CMD_SET_POWER
412 };
413
414 return _ExecCommand(I2C_OP_WRITE_STOP, cmd, sizeof(cmd), NULL, 0);
415 }
416
417
418 status_t
_ReadRegister(uint16_t reg,size_t length,void * value)419 ELANDevice::_ReadRegister(uint16_t reg, size_t length, void *value)
420 {
421 uint8_t cmd[2] = {
422 (uint8_t) (reg & 0xff), (uint8_t) ((reg >> 8) & 0xff) };
423 status_t status = _FetchBuffer(cmd, sizeof(cmd), fTransferBuffer,
424 length);
425 TRACE("Read register 0x%04x with value 0x%02x 0x%02x status=%d\n",
426 reg, fTransferBuffer[0], fTransferBuffer[1], status);
427 if (status != B_OK)
428 return status;
429 memcpy(value, fTransferBuffer, length);
430 return B_OK;
431 }
432
433
434 status_t
_WriteRegister(uint16_t reg,uint16_t value)435 ELANDevice::_WriteRegister(uint16_t reg, uint16_t value)
436 {
437 uint8_t cmd[4] = { (uint8_t) (reg & 0xff),
438 (uint8_t) ((reg >> 8) & 0xff),
439 (uint8_t) (value & 0xff),
440 (uint8_t) ((value >> 8) & 0xff) };
441 TRACE("Write register 0x%04x with value 0x%04x\n", reg, value);
442 status_t status = _FetchBuffer(cmd, sizeof(cmd), fTransferBuffer, 0);
443 TRACE("status=%d\n", status);
444 return status;
445 }
446
447
448 status_t
_FetchReport(uint8 type,uint8 id,size_t reportSize)449 ELANDevice::_FetchReport(uint8 type, uint8 id, size_t reportSize)
450 {
451 uint8 reportId = id > 15 ? 15 : id;
452 size_t cmdLength = 6;
453 uint8 cmd[] = {
454 (uint8)(fDescriptor.wCommandRegister & 0xff),
455 (uint8)(fDescriptor.wCommandRegister >> 8),
456 (uint8)(reportId | (type << 4)),
457 I2C_HID_CMD_GET_REPORT,
458 0, 0, 0,
459 };
460
461 int dataOffset = 4;
462 int reportIdLength = 1;
463 if (reportId == 15) {
464 cmd[dataOffset++] = id;
465 cmdLength++;
466 reportIdLength++;
467 }
468 cmd[dataOffset++] = fDescriptor.wDataRegister & 0xff;
469 cmd[dataOffset++] = fDescriptor.wDataRegister >> 8;
470
471 size_t bufferLength = reportSize + reportIdLength + 2;
472
473 status_t status = _FetchBuffer(cmd, cmdLength, fTransferBuffer,
474 bufferLength);
475 if (status != B_OK) {
476 atomic_set(&fTransferScheduled, 0);
477 return status;
478 }
479
480 uint16 actualLength = fTransferBuffer[0] | (fTransferBuffer[1] << 8);
481 TRACE("_FetchReport %" B_PRIuSIZE " %" B_PRIu16 "\n", reportSize,
482 actualLength);
483
484 if (actualLength <= 2 || actualLength == 0xffff || bufferLength == 0)
485 actualLength = 0;
486 else
487 actualLength -= 2;
488
489 atomic_set(&fTransferScheduled, 0);
490 _SetReport(status,
491 (uint8*)((addr_t)fTransferBuffer + 2), actualLength);
492 return B_OK;
493 }
494
495
496 void
_SetReport(status_t status,uint8 * report,size_t length)497 ELANDevice::_SetReport(status_t status, uint8 *report, size_t length)
498 {
499 if (status != B_OK) {
500 report = NULL;
501 length = 0;
502 }
503
504 if (length < MIN_ELAN_REPORT && length != 0 && status == B_OK)
505 status = B_ERROR;
506
507 if (status == B_OK && length != 0 && report[0] != fReportID) {
508 report = NULL;
509 length = 0;
510 status = B_INTERRUPTED;
511 }
512
513 if (report && length) {
514 report++;
515 length--;
516 }
517
518 fReportStatus = status;
519 fCurrentReportLength = length;
520 memset(fCurrentReport, 0, sizeof(fCurrentReport));
521 if (report && status == B_OK)
522 memcpy(fCurrentReport, report, MIN(sizeof(fCurrentReport), length));
523 fConditionVariable.NotifyAll();
524 }
525
526
527 status_t
_FetchBuffer(uint8 * cmd,size_t cmdLength,void * buffer,size_t bufferLength)528 ELANDevice::_FetchBuffer(uint8* cmd, size_t cmdLength, void* buffer,
529 size_t bufferLength)
530 {
531 return _ExecCommand(I2C_OP_READ_STOP, cmd, cmdLength,
532 buffer, bufferLength);
533 }
534
535
536 status_t
_ExecCommand(i2c_op op,uint8 * cmd,size_t cmdLength,void * buffer,size_t bufferLength)537 ELANDevice::_ExecCommand(i2c_op op, uint8* cmd, size_t cmdLength, void* buffer,
538 size_t bufferLength)
539 {
540 status_t status = fI2C->acquire_bus(fI2CCookie);
541 if (status != B_OK)
542 return status;
543 status = fI2C->exec_command(fI2CCookie, I2C_OP_READ_STOP, cmd, cmdLength,
544 buffer, bufferLength);
545 fI2C->release_bus(fI2CCookie);
546 return status;
547 }
548