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