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
TabletProtocolHandler(HIDReport & report,HIDReportItem & xAxis,HIDReportItem & yAxis)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
AddHandlers(HIDDevice & device,HIDCollection & collection,ProtocolHandler * & handlerList)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
Control(uint32 * cookie,uint32 op,void * buffer,size_t length)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
_ReadReport(void * buffer,uint32 * cookie)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