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