xref: /haiku/src/add-ons/kernel/drivers/input/hid_shared/TabletProtocolHandler.cpp (revision abba71575e34f84c3d75be9d29d6e6caa6c033db)
1 /*
2  * Copyright 2013 Stephan Aßmus <superstippi@gmx.de>
3  * Copyright 2010-2011 Enrique Medina Gremaldos <quiqueiii@gmail.com>
4  * Copyright 2008-2011 Michael Lotz <mmlr@mlotz.ch>
5  * Distributed under the terms of the MIT license.
6  */
7 
8 
9 //!	Driver for USB Human Interface Devices.
10 
11 
12 #include "Driver.h"
13 #include "TabletProtocolHandler.h"
14 
15 #include "HIDCollection.h"
16 #include "HIDDevice.h"
17 #include "HIDReport.h"
18 #include "HIDReportItem.h"
19 
20 #include <new>
21 #include <kernel.h>
22 #include <string.h>
23 #include <usb/USB_hid.h>
24 
25 #include <keyboard_mouse_driver.h>
26 
27 
28 TabletProtocolHandler::TabletProtocolHandler(HIDReport &report,
29 	HIDReportItem &xAxis, HIDReportItem &yAxis)
30 	:
31 	ProtocolHandler(report.Device(), "input/tablet/" DEVICE_PATH_SUFFIX "/",
32 		0),
33 	fReport(report),
34 
35 	fXAxis(xAxis),
36 	fYAxis(yAxis),
37 	fWheel(NULL),
38 
39 	fPressure(NULL),
40 	fInRange(NULL),
41 	fXTilt(NULL),
42 	fYTilt(NULL),
43 
44 	fLastButtons(0),
45 	fLastSwitches(0),
46 	fClickCount(0),
47 	fLastClickTime(0),
48 	fClickSpeed(250000)
49 {
50 	uint32 buttonCount = 0;
51 	uint32 switchCount = 0;
52 
53 	for (uint32 i = 0; i < report.CountItems(); i++) {
54 		HIDReportItem *item = report.ItemAt(i);
55 		if (!item->HasData())
56 			continue;
57 
58 		if (item->UsagePage() == B_HID_USAGE_PAGE_BUTTON
59 			&& item->UsageID() - 1 < B_MAX_MOUSE_BUTTONS) {
60 			fButtons[buttonCount++] = item;
61 		}
62 
63 		if (item->UsagePage() == B_HID_USAGE_PAGE_DIGITIZER
64 			&& item->UsageID() >= B_HID_UID_DIG_TIP_SWITCH
65 			&& item->UsageID() <= B_HID_UID_DIG_TABLET_PICK) {
66 			fSwitches[switchCount++] = item;
67 		}
68 	}
69 
70 	fButtons[buttonCount] = NULL;
71 	fSwitches[switchCount] = NULL;
72 
73 	fWheel = report.FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP,
74 		B_HID_UID_GD_WHEEL);
75 
76 	fPressure = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
77 		B_HID_UID_DIG_TIP_PRESSURE);
78 
79 	fInRange = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
80 		B_HID_UID_DIG_IN_RANGE);
81 
82 	fXTilt = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
83 		B_HID_UID_DIG_X_TILT);
84 
85 	fYTilt = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
86 		B_HID_UID_DIG_Y_TILT);
87 
88 	TRACE("tablet device with %" B_PRIu32 " buttons, %" B_PRIu32 " switches,"
89 		"%spressure, and %stilt\n",
90 		buttonCount,
91 		switchCount,
92 		fPressure == NULL ? "no " : "",
93 		fXTilt == NULL && fYTilt == NULL ? "no " : "");
94 
95 	TRACE("report id: %u\n", report.ID());
96 }
97 
98 
99 void
100 TabletProtocolHandler::AddHandlers(HIDDevice &device, HIDCollection &collection,
101 	ProtocolHandler *&handlerList)
102 {
103 	bool supported = false;
104 	switch (collection.UsagePage()) {
105 		case B_HID_USAGE_PAGE_GENERIC_DESKTOP:
106 		{
107 			switch (collection.UsageID()) {
108 				case B_HID_UID_GD_MOUSE:
109 				case B_HID_UID_GD_POINTER:
110 					// NOTE: Maybe it is supported if X-axis and Y-axis are
111 					// absolute. This is determined below by scanning the
112 					// report items for absolute X and Y axis.
113 					supported = true;
114 					break;
115 			}
116 
117 			break;
118 		}
119 
120 		case B_HID_USAGE_PAGE_DIGITIZER:
121 		{
122 			switch (collection.UsageID()) {
123 				case B_HID_UID_DIG_DIGITIZER:
124 				case B_HID_UID_DIG_PEN:
125 				case B_HID_UID_DIG_LIGHT_PEN:
126 				case B_HID_UID_DIG_TOUCH_SCREEN:
127 				case B_HID_UID_DIG_TOUCH_PAD:
128 				case B_HID_UID_DIG_WHITE_BOARD:
129 					TRACE("found tablet/digitizer\n");
130 					supported = true;
131 					break;
132 			}
133 
134 			break;
135 		}
136 	}
137 
138 	if (!supported) {
139 		TRACE("collection not a tablet/digitizer\n");
140 		return;
141 	}
142 
143 	HIDParser &parser = device.Parser();
144 	uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT);
145 	if (maxReportCount == 0)
146 		return;
147 
148 	uint32 inputReportCount = 0;
149 	HIDReport *inputReports[maxReportCount];
150 	collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports,
151 		inputReportCount);
152 
153 	for (uint32 i = 0; i < inputReportCount; i++) {
154 		HIDReport *inputReport = inputReports[i];
155 
156 		// try to find at least an absolute x and y axis
157 		HIDReportItem *xAxis = inputReport->FindItem(
158 			B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_X);
159 		if (xAxis == NULL || xAxis->Relative())
160 			continue;
161 
162 		HIDReportItem *yAxis = inputReport->FindItem(
163 			B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_Y);
164 		if (yAxis == NULL || yAxis->Relative())
165 			continue;
166 
167 		ProtocolHandler *newHandler = new(std::nothrow) TabletProtocolHandler(
168 			*inputReport, *xAxis, *yAxis);
169 		if (newHandler == NULL) {
170 			TRACE("failed to allocated tablet protocol handler\n");
171 			continue;
172 		}
173 
174 		newHandler->SetNextHandler(handlerList);
175 		handlerList = newHandler;
176 	}
177 }
178 
179 
180 status_t
181 TabletProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer,
182 	size_t length)
183 {
184 	switch (op) {
185 		case MS_READ:
186 		{
187 			if (length < sizeof(tablet_movement))
188 				return B_BUFFER_OVERFLOW;
189 
190 			while (true) {
191 				tablet_movement movement;
192 				status_t result = _ReadReport(&movement, cookie);
193 				if (result == B_INTERRUPTED)
194 					continue;
195 				if (!IS_USER_ADDRESS(buffer)
196 					|| user_memcpy(buffer, &movement, sizeof(movement))
197 						!= B_OK) {
198 					return B_BAD_ADDRESS;
199 				}
200 
201 				return result;
202 			}
203 		}
204 
205 		case MS_NUM_EVENTS:
206 		{
207 			if (fReport.Device()->IsRemoved())
208 				return B_DEV_NOT_READY;
209 
210 			// we are always on demand, so 0 queued events
211 			return 0;
212 		}
213 
214 		case MS_SET_CLICKSPEED:
215 				if (!IS_USER_ADDRESS(buffer)
216 					|| user_memcpy(&fClickSpeed, buffer, sizeof(bigtime_t))
217 						!= B_OK) {
218 					return B_BAD_ADDRESS;
219 				}
220 
221 				return B_OK;
222 	}
223 
224 	return B_ERROR;
225 }
226 
227 
228 status_t
229 TabletProtocolHandler::_ReadReport(void *buffer, uint32 *cookie)
230 {
231 	status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT);
232 	if (result != B_OK) {
233 		if (fReport.Device()->IsRemoved()) {
234 			TRACE("device has been removed\n");
235 			return B_DEV_NOT_READY;
236 		}
237 
238 		if ((*cookie & PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED) != 0)
239 			return B_CANCELED;
240 
241 		if (result != B_INTERRUPTED) {
242 			// interrupts happen when other reports come in on the same
243 			// input as ours
244 			TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
245 		}
246 
247 		// signal that we simply want to try again
248 		return B_INTERRUPTED;
249 	}
250 
251 	float axisAbsoluteData[2];
252 	axisAbsoluteData[0] = 0.0f;
253 	axisAbsoluteData[1] = 0.0f;
254 
255 	if (fXAxis.Extract() == B_OK && fXAxis.Valid())
256 		axisAbsoluteData[0] = fXAxis.ScaledFloatData();
257 
258 	if (fYAxis.Extract() == B_OK && fYAxis.Valid())
259 		axisAbsoluteData[1] = fYAxis.ScaledFloatData();
260 
261 	uint32 wheelData = 0;
262 	if (fWheel != NULL && fWheel->Extract() == B_OK && fWheel->Valid())
263 		wheelData = fWheel->Data();
264 
265 	uint32 buttons = 0;
266 	for (uint32 i = 0; i < B_MAX_MOUSE_BUTTONS; i++) {
267 		HIDReportItem *button = fButtons[i];
268 		if (button == NULL)
269 			break;
270 
271 		if (button->Extract() == B_OK && button->Valid())
272 			buttons |= (button->Data() & 1) << (button->UsageID() - 1);
273 	}
274 
275 	uint32 switches = 0;
276 	for (uint32 i = 0; i < B_MAX_DIGITIZER_SWITCHES; i++) {
277 		HIDReportItem *dswitch = fSwitches[i];
278 		if (dswitch == NULL)
279 			break;
280 
281 		if (dswitch->Extract() == B_OK && dswitch->Valid())
282 			switches |= (dswitch->Data() & 1) << (dswitch->UsageID()
283 				- B_HID_UID_DIG_TIP_SWITCH);
284 	}
285 
286 	float pressure = 1.0f;
287 	if (fPressure != NULL && fPressure->Extract() == B_OK
288 		&& fPressure->Valid()) {
289 		pressure = fPressure->ScaledFloatData();
290 	}
291 
292 	float xTilt = 0.0f;
293 	if (fXTilt != NULL && fXTilt->Extract() == B_OK && fXTilt->Valid())
294 		xTilt = fXTilt->ScaledFloatData();
295 
296 	float yTilt = 0.0f;
297 	if (fYTilt != NULL && fYTilt->Extract() == B_OK && fYTilt->Valid())
298 		yTilt = fYTilt->ScaledFloatData();
299 
300 	bool inRange = true;
301 	if (fInRange != NULL && fInRange->Extract() == B_OK && fInRange->Valid())
302 		inRange = ((fInRange->Data() & 1) != 0);
303 
304 	fReport.DoneProcessing();
305 	TRACE("got tablet report\n");
306 
307 	int32 clicks = 0;
308 	bigtime_t timestamp = system_time();
309 	if (buttons != 0) {
310 		if (fLastButtons == 0) {
311 			if (fLastClickTime + fClickSpeed > timestamp)
312 				fClickCount++;
313 			else
314 				fClickCount = 1;
315 		}
316 
317 		fLastClickTime = timestamp;
318 		clicks = fClickCount;
319 	}
320 
321 	fLastButtons = buttons;
322 
323 	if (switches != 0) {
324 		if (fLastSwitches == 0) {
325 			if (fLastClickTime + fClickSpeed > timestamp)
326 				fClickCount++;
327 			else
328 				fClickCount = 1;
329 		}
330 	}
331 
332 	fLastSwitches = switches;
333 
334 	tablet_movement *info = (tablet_movement *)buffer;
335 	memset(info, 0, sizeof(tablet_movement));
336 
337 	info->xpos = axisAbsoluteData[0];
338 	info->ypos = axisAbsoluteData[1];
339 	info->has_contact = inRange;
340 	info->pressure = pressure;
341 	info->tilt_x = xTilt;
342 	info->tilt_y = yTilt;
343 
344 	info->switches = switches;
345 	info->buttons = buttons;
346 	info->clicks = clicks;
347 	info->timestamp = timestamp;
348 	info->wheel_ydelta = -wheelData;
349 
350 	return B_OK;
351 }
352