xref: /haiku/src/add-ons/kernel/drivers/input/hid_shared/TabletProtocolHandler.cpp (revision 9e25244c5e9051f6cd333820d6332397361abd6c)
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 
186 		case B_GET_DEVICE_NAME:
187 		{
188 			const char name[] = DEVICE_NAME" Tablet";
189 			return IOGetDeviceName(name,buffer,length);
190 		}
191 
192 		case MS_READ:
193 		{
194 			if (length < sizeof(tablet_movement))
195 				return B_BUFFER_OVERFLOW;
196 
197 			while (true) {
198 				tablet_movement movement;
199 				status_t result = _ReadReport(&movement, cookie);
200 				if (result == B_INTERRUPTED)
201 					continue;
202 				if (!IS_USER_ADDRESS(buffer)
203 					|| user_memcpy(buffer, &movement, sizeof(movement))
204 						!= B_OK) {
205 					return B_BAD_ADDRESS;
206 				}
207 
208 				return result;
209 			}
210 		}
211 
212 		case MS_NUM_EVENTS:
213 		{
214 			if (fReport.Device()->IsRemoved())
215 				return B_DEV_NOT_READY;
216 
217 			// we are always on demand, so 0 queued events
218 			return 0;
219 		}
220 
221 		case MS_SET_CLICKSPEED:
222 				if (!IS_USER_ADDRESS(buffer)
223 					|| user_memcpy(&fClickSpeed, buffer, sizeof(bigtime_t))
224 						!= B_OK) {
225 					return B_BAD_ADDRESS;
226 				}
227 
228 				return B_OK;
229 	}
230 
231 	return B_ERROR;
232 }
233 
234 
235 status_t
236 TabletProtocolHandler::_ReadReport(void *buffer, uint32 *cookie)
237 {
238 	status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT);
239 	if (result != B_OK) {
240 		if (fReport.Device()->IsRemoved()) {
241 			TRACE("device has been removed\n");
242 			return B_DEV_NOT_READY;
243 		}
244 
245 		if ((*cookie & PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED) != 0)
246 			return B_CANCELED;
247 
248 		if (result != B_INTERRUPTED) {
249 			// interrupts happen when other reports come in on the same
250 			// input as ours
251 			TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
252 		}
253 
254 		// signal that we simply want to try again
255 		return B_INTERRUPTED;
256 	}
257 
258 	float axisAbsoluteData[2];
259 	axisAbsoluteData[0] = 0.0f;
260 	axisAbsoluteData[1] = 0.0f;
261 
262 	if (fXAxis.Extract() == B_OK && fXAxis.Valid())
263 		axisAbsoluteData[0] = fXAxis.ScaledFloatData();
264 
265 	if (fYAxis.Extract() == B_OK && fYAxis.Valid())
266 		axisAbsoluteData[1] = fYAxis.ScaledFloatData();
267 
268 	uint32 wheelData = 0;
269 	if (fWheel != NULL && fWheel->Extract() == B_OK && fWheel->Valid())
270 		wheelData = fWheel->Data();
271 
272 	uint32 buttons = 0;
273 	for (uint32 i = 0; i < B_MAX_MOUSE_BUTTONS; i++) {
274 		HIDReportItem *button = fButtons[i];
275 		if (button == NULL)
276 			break;
277 
278 		if (button->Extract() == B_OK && button->Valid())
279 			buttons |= (button->Data() & 1) << (button->UsageID() - 1);
280 	}
281 
282 	uint32 switches = 0;
283 	for (uint32 i = 0; i < B_MAX_DIGITIZER_SWITCHES; i++) {
284 		HIDReportItem *dswitch = fSwitches[i];
285 		if (dswitch == NULL)
286 			break;
287 
288 		if (dswitch->Extract() == B_OK && dswitch->Valid())
289 			switches |= (dswitch->Data() & 1) << (dswitch->UsageID()
290 				- B_HID_UID_DIG_TIP_SWITCH);
291 	}
292 
293 	float pressure = 1.0f;
294 	if (fPressure != NULL && fPressure->Extract() == B_OK
295 		&& fPressure->Valid()) {
296 		pressure = fPressure->ScaledFloatData();
297 	}
298 
299 	float xTilt = 0.0f;
300 	if (fXTilt != NULL && fXTilt->Extract() == B_OK && fXTilt->Valid())
301 		xTilt = fXTilt->ScaledFloatData();
302 
303 	float yTilt = 0.0f;
304 	if (fYTilt != NULL && fYTilt->Extract() == B_OK && fYTilt->Valid())
305 		yTilt = fYTilt->ScaledFloatData();
306 
307 	bool inRange = true;
308 	if (fInRange != NULL && fInRange->Extract() == B_OK && fInRange->Valid())
309 		inRange = ((fInRange->Data() & 1) != 0);
310 
311 	fReport.DoneProcessing();
312 	TRACE("got tablet report\n");
313 
314 	int32 clicks = 0;
315 	bigtime_t timestamp = system_time();
316 	if (buttons != 0) {
317 		if (fLastButtons == 0) {
318 			if (fLastClickTime + fClickSpeed > timestamp)
319 				fClickCount++;
320 			else
321 				fClickCount = 1;
322 		}
323 
324 		fLastClickTime = timestamp;
325 		clicks = fClickCount;
326 	}
327 
328 	fLastButtons = buttons;
329 
330 	if (switches != 0) {
331 		if (fLastSwitches == 0) {
332 			if (fLastClickTime + fClickSpeed > timestamp)
333 				fClickCount++;
334 			else
335 				fClickCount = 1;
336 		}
337 	}
338 
339 	fLastSwitches = switches;
340 
341 	tablet_movement *info = (tablet_movement *)buffer;
342 	memset(info, 0, sizeof(tablet_movement));
343 
344 	info->xpos = axisAbsoluteData[0];
345 	info->ypos = axisAbsoluteData[1];
346 	info->has_contact = inRange;
347 	info->pressure = pressure;
348 	info->tilt_x = xTilt;
349 	info->tilt_y = yTilt;
350 
351 	info->switches = switches;
352 	info->buttons = buttons;
353 	info->clicks = clicks;
354 	info->timestamp = timestamp;
355 	info->wheel_ydelta = -wheelData;
356 
357 	return B_OK;
358 }
359