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 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 104 ELANDevice::~ELANDevice() 105 { 106 } 107 108 109 status_t 110 ELANDevice::Open(uint32 flags) 111 { 112 atomic_add(&fOpenCount, 1); 113 _Reset(); 114 115 return B_OK; 116 } 117 118 119 status_t 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 130 ELANDevice::Removed() 131 { 132 fRemoved = true; 133 } 134 135 136 status_t 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 156 ELANDevice::SetPublishPath(char *publishPath) 157 { 158 free(fPublishPath); 159 fPublishPath = publishPath; 160 } 161 162 status_t 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 223 ELANDevice::_SetAbsoluteMode(bool enable) 224 { 225 return _WriteRegister(IETP_CONTROL, enable ? IETP_CTRL_ABSOLUTE : IETP_CTRL_STANDARD); 226 } 227 228 229 status_t 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 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 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 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 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 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 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 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 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 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 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 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