xref: /haiku/src/add-ons/kernel/drivers/input/i2c_elan/ELANDevice.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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